// 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. Load more
// 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);