/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
#include "fuse_i.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/uio.h>
#include <linux/miscdevice.h>
#include <linux/pagemap.h>
#include <linux/file.h>
#include <linux/slab.h>
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
static kmem_cache_t *fuse_req_cachep;
static struct fuse_conn *fuse_get_conn(struct file *file)
{
struct fuse_conn *fc;
spin_lock(&fuse_lock);
fc = file->private_data;
if (fc && !fc->connected)
fc = NULL;
spin_unlock(&fuse_lock);
return fc;
}
static void fuse_request_init(struct fuse_req *req)
{
memset(req, 0, sizeof(*req));
INIT_LIST_HEAD(&req->list);
init_waitqueue_head(&req->waitq);
atomic_set(&req->count, 1);
}
struct fuse_req *fuse_request_alloc(void)
{
struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, SLAB_KERNEL);
if (req)
fuse_request_init(req);
return req;
}
void fuse_request_free(struct fuse_req *req)
{
kmem_cache_free(fuse_req_cachep, req);
}
static void block_sigs(sigset_t *oldset)
{
sigset_t mask;
siginitsetinv(&mask, sigmask(SIGKILL));
sigprocmask(SIG_BLOCK, &mask, oldset);
}
static void restore_sigs(sigset_t *oldset)
{
sigprocmask(SIG_SETMASK, oldset, NULL);
}
/*
* Reset request, so that it can be reused
*
* The caller must be _very_ careful to make sure, that it is holding
* the only reference to req
*/
void fuse_reset_request(struct fuse_req *req)
{
int preallocated = req->preallocated;
BUG_ON(atomic_read(&req->count) != 1);
fuse_request_init(req);
req->preallocated = preallocated;
}
static void __fuse_get_request(struct fuse_req *req)
{
atomic_inc(&req->count);
}
/* Must be called with > 1 refcount */
static void __fuse_put_request(struct fuse_req *req)
{
BUG_ON(atomic_read(&req->count) < 2);
atomic_dec(&req->count);
}
static struct fuse_req *do_get_request(struct fuse_conn *fc)
{
struct fuse_req *req;
spin_lock(&fuse_lock);
BUG_ON(list_empty(&fc->unused_list));
req = list_entry(fc->unused_list.next, struct fuse_req, list);
list_del_init(&req->list);
spin_unlock(&fuse_lock);
fuse_request_init(req);
req->preallocated = 1;
req->in.h.uid = current->fsuid;
req->in.h.gid = current->fsgid;
req->in.h.pid = current->pid;
return req;
}
/* This can return NULL, but only in case it's interrupted by a SIGKILL */
struct fuse_req *fuse_get_request(struct fuse_conn *fc)
{
int intr;
sigset_t oldset;
atomic_inc(&fc->num_waiting);
block_sigs(&oldset);
intr = down_interruptible(&fc->outstanding_sem);
restore_sigs(&oldset);
if (intr) {
atomic_dec(&fc->num_waiting);
return NULL;
}
return do_get_request(fc);
}
/* Must be called with fuse_lock held */
static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (req->preallocated) {
atomic_dec(&fc->num_waiting);
list_add(&req->list, &fc->unused_list);
} else
fuse_request_free(req);
/* If we are in debt decrease that first */
if (fc->outstanding_debt)
fc->outstanding_debt--;
else
up(&fc->outstanding_sem);
}
void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count)) {
spin_lock(&fuse_lock);
fuse_putback_request(fc, req);
spin_unlock(&fuse_lock);
}
}
static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req)
{
if (atomic_dec_and_test(&req->count))
fuse_putback_request(fc, req);
}
void fuse_release_background(struct fuse_req *req)
{
iput(req->inode);
iput(req->inode2);
if (req->file)
fput(req->file);
spin_lock(