3131 lines · 78.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/*  FUSE: Filesystem in Userspace  Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>*/ #include "fuse_i.h"#include "dev.h" #include <linux/pagemap.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/sched/signal.h>#include <linux/module.h>#include <linux/swap.h>#include <linux/falloc.h>#include <linux/uio.h>#include <linux/fs.h>#include <linux/filelock.h>#include <linux/splice.h>#include <linux/task_io_accounting_ops.h>#include <linux/iomap.h> static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,			  unsigned int open_flags, int opcode,			  struct fuse_open_out *outargp){	struct fuse_open_in inarg;	FUSE_ARGS(args); 	memset(&inarg, 0, sizeof(inarg));	inarg.flags = open_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);	if (!fm->fc->atomic_o_trunc)		inarg.flags &= ~O_TRUNC; 	if (fm->fc->handle_killpriv_v2 &&	    (inarg.flags & O_TRUNC) && !capable(CAP_FSETID)) {		inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;	} 	args.opcode = opcode;	args.nodeid = nodeid;	args.in_numargs = 1;	args.in_args[0].size = sizeof(inarg);	args.in_args[0].value = &inarg;	args.out_numargs = 1;	args.out_args[0].size = sizeof(*outargp);	args.out_args[0].value = outargp; 	return fuse_simple_request(fm, &args);} struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release){	struct fuse_file *ff; 	ff = kzalloc_obj(struct fuse_file, GFP_KERNEL_ACCOUNT);	if (unlikely(!ff))		return NULL; 	ff->fm = fm;	if (release) {		ff->args = kzalloc_obj(*ff->args, GFP_KERNEL_ACCOUNT);		if (!ff->args) {			kfree(ff);			return NULL;		}	} 	INIT_LIST_HEAD(&ff->write_entry);	refcount_set(&ff->count, 1);	RB_CLEAR_NODE(&ff->polled_node);	init_waitqueue_head(&ff->poll_wait); 	ff->kh = atomic64_inc_return(&fm->fc->khctr); 	return ff;} void fuse_file_free(struct fuse_file *ff){	kfree(ff->args);	kfree(ff);} static struct fuse_file *fuse_file_get(struct fuse_file *ff){	refcount_inc(&ff->count);	return ff;} static void fuse_release_end(struct fuse_args *args, int error){	struct fuse_release_args *ra = container_of(args, typeof(*ra), args); 	iput(ra->inode);	kfree(ra);} static void fuse_file_put(struct fuse_file *ff, bool sync){	if (refcount_dec_and_test(&ff->count)) {		struct fuse_release_args *ra = &ff->args->release_args;		struct fuse_args *args = (ra ? &ra->args : NULL); 		if (ra && ra->inode)			fuse_file_io_release(ff, ra->inode); 		if (!args) {			/* Do nothing when server does not implement 'opendir' */		} else if (args->opcode == FUSE_RELEASE && ff->fm->fc->no_open) {			fuse_release_end(args, 0);		} else if (sync) {			fuse_simple_request(ff->fm, args);			fuse_release_end(args, 0);		} else {			/*			 * DAX inodes may need to issue a number of synchronous			 * request for clearing the mappings.			 */			if (ra && ra->inode && FUSE_IS_DAX(ra->inode))				args->may_block = true;			args->end = fuse_release_end;			if (fuse_simple_background(ff->fm, args,						   GFP_KERNEL | __GFP_NOFAIL))				fuse_release_end(args, -ENOTCONN);		}		kfree(ff);	}} struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,				 unsigned int open_flags, bool isdir){	struct fuse_conn *fc = fm->fc;	struct fuse_file *ff;	int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;	bool open = isdir ? !fc->no_opendir : !fc->no_open;	bool release = !isdir || open; 	/*	 * ff->args->release_args still needs to be allocated (so we can hold an	 * inode reference while there are pending inflight file operations when	 * ->release() is called, see fuse_prepare_release()) even if	 * fc->no_open is set else it becomes possible for reclaim to deadlock	 * if while servicing the readahead request the server triggers reclaim	 * and reclaim evicts the inode of the file being read ahead.	 */	ff = fuse_file_alloc(fm, release);	if (!ff)		return ERR_PTR(-ENOMEM); 	ff->fh = 0;	/* Default for no-open */	ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0);	if (open) {		/* Store outarg for fuse_finish_open() */		struct fuse_open_out *outargp = &ff->args->open_outarg;		int err; 		err = fuse_send_open(fm, nodeid, open_flags, opcode, outargp);		if (!err) {			ff->fh = outargp->fh;			ff->open_flags = outargp->open_flags;		} else if (err != -ENOSYS) {			fuse_file_free(ff);			return ERR_PTR(err);		} else {			if (isdir) {				/* No release needed */				kfree(ff->args);				ff->args = NULL;				fc->no_opendir = 1;			} else {				fc->no_open = 1;			}		}	} 	if (isdir)		ff->open_flags &= ~FOPEN_DIRECT_IO; 	ff->nodeid = nodeid; 	return ff;} int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,		 bool isdir){	struct fuse_file *ff = fuse_file_open(fm, nodeid, file->f_flags, isdir); 	if (!IS_ERR(ff))		file->private_data = ff; 	return PTR_ERR_OR_ZERO(ff);}EXPORT_SYMBOL_GPL(fuse_do_open); static void fuse_link_write_file(struct file *file){	struct inode *inode = file_inode(file);	struct fuse_inode *fi = get_fuse_inode(inode);	struct fuse_file *ff = file->private_data;	/*	 * file may be written through mmap, so chain it onto the	 * inodes's write_file list	 */	spin_lock(&fi->lock);	if (list_empty(&ff->write_entry))		list_add(&ff->write_entry, &fi->write_files);	spin_unlock(&fi->lock);} int fuse_finish_open(struct inode *inode, struct file *file){	struct fuse_file *ff = file->private_data;	struct fuse_conn *fc = get_fuse_conn(inode);	int err; 	err = fuse_file_io_open(file, inode);	if (err)		return err; 	if (ff->open_flags & FOPEN_STREAM)		stream_open(inode, file);	else if (ff->open_flags & FOPEN_NONSEEKABLE)		nonseekable_open(inode, file); 	if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)		fuse_link_write_file(file); 	return 0;} static void fuse_truncate_update_attr(struct inode *inode, struct file *file){	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);	i_size_write(inode, 0);	spin_unlock(&fi->lock);	file_update_time(file);	fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);} static int fuse_open(struct inode *inode, struct file *file){	struct fuse_mount *fm = get_fuse_mount(inode);	struct fuse_inode *fi = get_fuse_inode(inode);	struct fuse_conn *fc = fm->fc;	struct fuse_file *ff;	int err;	bool is_truncate = (file->f_flags & O_TRUNC) && fc->atomic_o_trunc;	bool is_wb_truncate = is_truncate && fc->writeback_cache;	bool dax_truncate = is_truncate && FUSE_IS_DAX(inode); 	if (fuse_is_bad(inode))		return -EIO; 	err = generic_file_open(inode, file);	if (err)		return err; 	if (is_wb_truncate || dax_truncate)		inode_lock(inode); 	if (dax_truncate) {		filemap_invalidate_lock(inode->i_mapping);		err = fuse_dax_break_layouts(inode, 0, -1);		if (err)			goto out_unlock;	} 	if (is_wb_truncate || dax_truncate)		fuse_set_nowrite(inode); 	err = fuse_do_open(fm, get_node_id(inode), file, false);	if (!err) {		ff = file->private_data;		err = fuse_finish_open(inode, file);		if (err)			fuse_sync_release(fi, ff, file->f_flags);		else if (is_truncate)			fuse_truncate_update_attr(inode, file);	} 	if (is_wb_truncate || dax_truncate)		fuse_release_nowrite(inode);	if (!err) {		if (is_truncate)			truncate_pagecache(inode, 0);		else if (!(ff->open_flags & FOPEN_KEEP_CACHE))			invalidate_inode_pages2(inode->i_mapping);	}out_unlock:	if (dax_truncate)		filemap_invalidate_unlock(inode->i_mapping);	if (is_wb_truncate || dax_truncate)		inode_unlock(inode); 	return err;} static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,				 unsigned int flags, int opcode, bool sync){	struct fuse_conn *fc = ff->fm->fc;	struct fuse_release_args *ra = &ff->args->release_args; 	if (fuse_file_passthrough(ff))		fuse_passthrough_release(ff, fuse_inode_backing(fi)); 	/* Inode is NULL on error path of fuse_create_open() */	if (likely(fi)) {		spin_lock(&fi->lock);		list_del(&ff->write_entry);		spin_unlock(&fi->lock);	}	spin_lock(&fc->lock);	if (!RB_EMPTY_NODE(&ff->polled_node))		rb_erase(&ff->polled_node, &fc->polled_files);	spin_unlock(&fc->lock); 	wake_up_interruptible_all(&ff->poll_wait); 	if (!ra)		return; 	/* ff->args was used for open outarg */	memset(ff->args, 0, sizeof(*ff->args));	ra->inarg.fh = ff->fh;	ra->inarg.flags = flags;	ra->args.in_numargs = 1;	ra->args.in_args[0].size = sizeof(struct fuse_release_in);	ra->args.in_args[0].value = &ra->inarg;	ra->args.opcode = opcode;	ra->args.nodeid = ff->nodeid;	ra->args.force = true;	ra->args.nocreds = true; 	/*	 * Hold inode until release is finished.	 * From fuse_sync_release() the refcount is 1 and everything's	 * synchronous, so we are fine with not doing igrab() here.	 */	ra->inode = sync ? NULL : igrab(&fi->inode);} void fuse_file_release(struct inode *inode, struct fuse_file *ff,		       unsigned int open_flags, fl_owner_t id, bool isdir){	struct fuse_inode *fi = get_fuse_inode(inode);	struct fuse_release_args *ra = &ff->args->release_args;	int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; 	fuse_prepare_release(fi, ff, open_flags, opcode, false); 	if (ra && ff->flock) {		ra->inarg.release_flags |= FUSE_RELEASE_FLOCK_UNLOCK;		ra->inarg.lock_owner = fuse_lock_owner_id(ff->fm->fc, id);	} 	/*	 * Normally this will send the RELEASE request, however if	 * some asynchronous READ or WRITE requests are outstanding,	 * the sending will be delayed.	 *	 * Make the release synchronous if this is a fuseblk mount,	 * synchronous RELEASE is allowed (and desirable) in this case	 * because the server can be trusted not to screw up.	 *	 * Always use the asynchronous file put because the current thread	 * might be the fuse server.  This can happen if a process starts some	 * aio and closes the fd before the aio completes.  Since aio takes its	 * own ref to the file, the IO completion has to drop the ref, which is	 * how the fuse server can end up closing its clients' files.	 *	 * Exception is virtio-fs, which is not affected by the above (server is	 * on host, cannot close open files in guest).  Virtio-fs needs sync	 * release, because the num_waiting mechanism to wait for all requests	 * before commencing with fs shutdown doesn't work if submounts are	 * used.	 */	fuse_file_put(ff, ff->fm->fc->auto_submounts);} void fuse_release_common(struct file *file, bool isdir){	fuse_file_release(file_inode(file), file->private_data, file->f_flags,			  (fl_owner_t) file, isdir);} static int fuse_release(struct inode *inode, struct file *file){	struct fuse_conn *fc = get_fuse_conn(inode); 	/*	 * Dirty pages might remain despite write_inode_now() call from	 * fuse_flush() due to writes racing with the close.	 */	if (fc->writeback_cache)		write_inode_now(inode, 1); 	fuse_release_common(file, false); 	/* return value is ignored by VFS */	return 0;} void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff,		       unsigned int flags){	WARN_ON(refcount_read(&ff->count) > 1);	fuse_prepare_release(fi, ff, flags, FUSE_RELEASE, true);	fuse_file_put(ff, true);}EXPORT_SYMBOL_GPL(fuse_sync_release); /* * Scramble the ID space with XTEA, so that the value of the files_struct * pointer is not exposed to userspace. */u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id){	u32 *k = fc->scramble_key;	u64 v = (unsigned long) id;	u32 v0 = v;	u32 v1 = v >> 32;	u32 sum = 0;	int i; 	for (i = 0; i < 32; i++) {		v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]);		sum += 0x9E3779B9;		v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]);	} 	return (u64) v0 + ((u64) v1 << 32);} struct fuse_writepage_args {	struct fuse_io_args ia;	struct list_head queue_entry;	struct inode *inode;	struct fuse_sync_bucket *bucket;}; /* * Wait for all pending writepages on the inode to finish. * * This is currently done by blocking further writes with FUSE_NOWRITE * and waiting for all sent writes to complete. * * This must be called under i_mutex, otherwise the FUSE_NOWRITE usage * could conflict with truncation. */static void fuse_sync_writes(struct inode *inode){	fuse_set_nowrite(inode);	fuse_release_nowrite(inode);} static int fuse_flush(struct file *file, fl_owner_t id){	struct inode *inode = file_inode(file);	struct fuse_mount *fm = get_fuse_mount(inode);	struct fuse_file *ff = file->private_data;	struct fuse_flush_in inarg;	FUSE_ARGS(args);	int err; 	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; 	err = filemap_check_errors(file->f_mapping);	if (err)		return err; 	err = 0;	if (fm->fc->no_flush)		goto inval_attr_out; 	memset(&inarg, 0, sizeof(inarg));	inarg.fh = ff->fh;	inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);	args.opcode = FUSE_FLUSH;	args.nodeid = get_node_id(inode);	args.in_numargs = 1;	args.in_args[0].size = sizeof(inarg);

Lines 1–500 of 3131.