linux/fs/fuse/file.c

/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2001-2008  Miklos Szeredi <[email protected]>

  This program can be distributed under the terms of the GNU GPL.
  See the file COPYING.
*/

#include "fuse_i.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>

static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
			  unsigned int open_flags, int opcode,
			  struct fuse_open_out *outargp)
{}

struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
{}

void fuse_file_free(struct fuse_file *ff)
{}

static struct fuse_file *fuse_file_get(struct fuse_file *ff)
{}

static void fuse_release_end(struct fuse_mount *fm, struct fuse_args *args,
			     int error)
{}

static void fuse_file_put(struct fuse_file *ff, bool sync)
{}

struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
				 unsigned int open_flags, bool isdir)
{}

int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
		 bool isdir)
{}
EXPORT_SYMBOL_GPL();

static void fuse_link_write_file(struct file *file)
{}

int fuse_finish_open(struct inode *inode, struct file *file)
{}

static void fuse_truncate_update_attr(struct inode *inode, struct file *file)
{}

static int fuse_open(struct inode *inode, struct file *file)
{}

static void fuse_prepare_release(struct fuse_inode *fi, struct fuse_file *ff,
				 unsigned int flags, int opcode, bool sync)
{}

void fuse_file_release(struct inode *inode, struct fuse_file *ff,
		       unsigned int open_flags, fl_owner_t id, bool isdir)
{}

void fuse_release_common(struct file *file, bool isdir)
{}

static int fuse_release(struct inode *inode, struct file *file)
{}

void fuse_sync_release(struct fuse_inode *fi, struct fuse_file *ff,
		       unsigned int flags)
{}
EXPORT_SYMBOL_GPL();

/*
 * 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)
{}

struct fuse_writepage_args {};

static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi,
					    pgoff_t idx_from, pgoff_t idx_to)
{}

/*
 * Check if any page in a range is under writeback
 *
 * This is currently done by walking the list of writepage requests
 * for the inode, which can be pretty inefficient.
 */
static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from,
				   pgoff_t idx_to)
{}

static inline bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
{}

/*
 * Wait for page writeback to be completed.
 *
 * Since fuse doesn't rely on the VM writeback tracking, this has to
 * use some other means.
 */
static void fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
{}

/*
 * 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)
{}

static int fuse_flush(struct file *file, fl_owner_t id)
{}

int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
		      int datasync, int opcode)
{}

static int fuse_fsync(struct file *file, loff_t start, loff_t end,
		      int datasync)
{}

void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos,
			 size_t count, int opcode)
{}

static void fuse_release_user_pages(struct fuse_args_pages *ap,
				    bool should_dirty)
{}

static void fuse_io_release(struct kref *kref)
{}

static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
{}

/*
 * In case of short read, the caller sets 'pos' to the position of
 * actual end of fuse request in IO request. Otherwise, if bytes_requested
 * == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
 *
 * An example:
 * User requested DIO read of 64K. It was split into two 32K fuse requests,
 * both submitted asynchronously. The first of them was ACKed by userspace as
 * fully completed (req->out.args[0].size == 32K) resulting in pos == -1. The
 * second request was ACKed as short, e.g. only 1K was read, resulting in
 * pos == 33K.
 *
 * Thus, when all fuse requests are completed, the minimal non-negative 'pos'
 * will be equal to the length of the longest contiguous fragment of
 * transferred data starting from the beginning of IO request.
 */
static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
{}

static struct fuse_io_args *fuse_io_alloc(struct fuse_io_priv *io,
					  unsigned int npages)
{}

static void fuse_io_free(struct fuse_io_args *ia)
{}

static void fuse_aio_complete_req(struct fuse_mount *fm, struct fuse_args *args,
				  int err)
{}

static ssize_t fuse_async_req_send(struct fuse_mount *fm,
				   struct fuse_io_args *ia, size_t num_bytes)
{}

static ssize_t fuse_send_read(struct fuse_io_args *ia, loff_t pos, size_t count,
			      fl_owner_t owner)
{}

static void fuse_read_update_size(struct inode *inode, loff_t size,
				  u64 attr_ver)
{}

static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
			    struct fuse_args_pages *ap)
{}

static int fuse_do_readpage(struct file *file, struct page *page)
{}

static int fuse_read_folio(struct file *file, struct folio *folio)
{}

static void fuse_readpages_end(struct fuse_mount *fm, struct fuse_args *args,
			       int err)
{}

static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file)
{}

static void fuse_readahead(struct readahead_control *rac)
{}

static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
{}

static void fuse_write_args_fill(struct fuse_io_args *ia, struct fuse_file *ff,
				 loff_t pos, size_t count)
{}

static unsigned int fuse_write_flags(struct kiocb *iocb)
{}

static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos,
			       size_t count, fl_owner_t owner)
{}

bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written)
{}

static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
				     struct kiocb *iocb, struct inode *inode,
				     loff_t pos, size_t count)
{}

static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
				     struct address_space *mapping,
				     struct iov_iter *ii, loff_t pos,
				     unsigned int max_pages)
{}

static inline unsigned int fuse_wr_pages(loff_t pos, size_t len,
				     unsigned int max_pages)
{}

static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii)
{}

static bool fuse_io_past_eof(struct kiocb *iocb, struct iov_iter *iter)
{}

/*
 * @return true if an exclusive lock for direct IO writes is needed
 */
static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from)
{}

static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
			  bool *exclusive)
{}

static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
{}

static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
{}

static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
{}

static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
					size_t max_size)
{}

static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
			       size_t *nbytesp, int write,
			       unsigned int max_pages)
{}

ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
		       loff_t *ppos, int flags)
{}
EXPORT_SYMBOL_GPL();

static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
				  struct iov_iter *iter,
				  loff_t *ppos)
{}

static ssize_t fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter);

static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to)
{}

static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
{}

static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{}

static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{}

static ssize_t fuse_splice_read(struct file *in, loff_t *ppos,
				struct pipe_inode_info *pipe, size_t len,
				unsigned int flags)
{}

static ssize_t fuse_splice_write(struct pipe_inode_info *pipe, struct file *out,
				 loff_t *ppos, size_t len, unsigned int flags)
{}

static void fuse_writepage_free(struct fuse_writepage_args *wpa)
{}

static void fuse_writepage_finish(struct fuse_mount *fm,
				  struct fuse_writepage_args *wpa)
{}

/* Called under fi->lock, may release and reacquire it */
static void fuse_send_writepage(struct fuse_mount *fm,
				struct fuse_writepage_args *wpa, loff_t size)
__releases(fi->lock)
__acquires(fi->lock)
{}

/*
 * If fi->writectr is positive (no truncate or fsync going on) send
 * all queued writepage requests.
 *
 * Called with fi->lock
 */
void fuse_flush_writepages(struct inode *inode)
__releases(fi->lock)
__acquires(fi->lock)
{}

static struct fuse_writepage_args *fuse_insert_writeback(struct rb_root *root,
						struct fuse_writepage_args *wpa)
{}

static void tree_insert(struct rb_root *root, struct fuse_writepage_args *wpa)
{}

static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
			       int error)
{}

static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi)
{}

static struct fuse_file *fuse_write_file_get(struct fuse_inode *fi)
{}

int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
{}

static struct fuse_writepage_args *fuse_writepage_args_alloc(void)
{}

static void fuse_writepage_add_to_bucket(struct fuse_conn *fc,
					 struct fuse_writepage_args *wpa)
{}

static int fuse_writepage_locked(struct folio *folio)
{}

struct fuse_fill_wb_data {};

static bool fuse_pages_realloc(struct fuse_fill_wb_data *data)
{}

static void fuse_writepages_send(struct fuse_fill_wb_data *data)
{}

/*
 * Check under fi->lock if the page is under writeback, and insert it onto the
 * rb_tree if not. Otherwise iterate auxiliary write requests, to see if there's
 * one already added for a page at this offset.  If there's none, then insert
 * this new request onto the auxiliary list, otherwise reuse the existing one by
 * swapping the new temp page with the old one.
 */
static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
			       struct page *page)
{}

static bool fuse_writepage_need_send(struct fuse_conn *fc, struct page *page,
				     struct fuse_args_pages *ap,
				     struct fuse_fill_wb_data *data)
{}

static int fuse_writepages_fill(struct folio *folio,
		struct writeback_control *wbc, void *_data)
{}

static int fuse_writepages(struct address_space *mapping,
			   struct writeback_control *wbc)
{}

/*
 * It's worthy to make sure that space is reserved on disk for the write,
 * but how to implement it without killing performance need more thinking.
 */
static int fuse_write_begin(struct file *file, struct address_space *mapping,
		loff_t pos, unsigned len, struct page **pagep, void **fsdata)
{}

static int fuse_write_end(struct file *file, struct address_space *mapping,
		loff_t pos, unsigned len, unsigned copied,
		struct page *page, void *fsdata)
{}

static int fuse_launder_folio(struct folio *folio)
{}

/*
 * 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)
{}

/*
 * Wait for writeback against this page to complete before allowing it
 * to be marked dirty again, and hence written back again, possibly
 * before the previous writepage completed.
 *
 * Block here, instead of in ->writepage(), so that the userspace fs
 * can only block processes actually operating on the filesystem.
 *
 * Otherwise unprivileged userspace fs would be able to block
 * unrelated:
 *
 * - page migration
 * - sync(2)
 * - try_to_free_pages() with order > PAGE_ALLOC_COSTLY_ORDER
 */
static vm_fault_t fuse_page_mkwrite(struct vm_fault *vmf)
{}

static const struct vm_operations_struct fuse_file_vm_ops =;

static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
{}

static int convert_fuse_file_lock(struct fuse_conn *fc,
				  const struct fuse_file_lock *ffl,
				  struct file_lock *fl)
{}

static void fuse_lk_fill(struct fuse_args *args, struct file *file,
			 const struct file_lock *fl, int opcode, pid_t pid,
			 int flock, struct fuse_lk_in *inarg)
{}

static int fuse_getlk(struct file *file, struct file_lock *fl)
{}

static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
{}

static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
{}

static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
{}

static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
{}

static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
{}

static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
{}

/*
 * All files which have been polled are linked to RB tree
 * fuse_conn->polled_files which is indexed by kh.  Walk the tree and
 * find the matching one.
 */
static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh,
					      struct rb_node **parent_out)
{}

/*
 * The file is about to be polled.  Make sure it's on the polled_files
 * RB tree.  Note that files once added to the polled_files tree are
 * not removed before the file is released.  This is because a file
 * polled once is likely to be polled again.
 */
static void fuse_register_polled_file(struct fuse_conn *fc,
				      struct fuse_file *ff)
{}

__poll_t fuse_file_poll(struct file *file, poll_table *wait)
{}
EXPORT_SYMBOL_GPL();

/*
 * This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
 * wakes up the poll waiters.
 */
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
			    struct fuse_notify_poll_wakeup_out *outarg)
{}

static void fuse_do_truncate(struct file *file)
{}

static inline loff_t fuse_round_up(struct fuse_conn *fc, loff_t off)
{}

static ssize_t
fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{}

static int fuse_writeback_range(struct inode *inode, loff_t start, loff_t end)
{}

static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
				loff_t length)
{}

static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
				      struct file *file_out, loff_t pos_out,
				      size_t len, unsigned int flags)
{}

static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off,
				    struct file *dst_file, loff_t dst_off,
				    size_t len, unsigned int flags)
{}

static const struct file_operations fuse_file_operations =;

static const struct address_space_operations fuse_file_aops  =;

void fuse_init_file_inode(struct inode *inode, unsigned int flags)
{}