[PATCH] FUSE - mount options
This patch adds miscellaneous mount options to the FUSE filesystem. The following mount options are added: o default_permissions: check permissions with generic_permission() o allow_other: allow other users to access files o allow_root: allow root to access files o kernel_cache: don't invalidate page cache on open Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
committed by
Linus Torvalds
parent
b6aeadeda2
commit
1e9a4ed939
@@ -26,7 +26,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file)
|
|||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc;
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
fc = file->private_data;
|
fc = file->private_data;
|
||||||
if (fc && !fc->sb)
|
if (fc && !fc->mounted)
|
||||||
fc = NULL;
|
fc = NULL;
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
return fc;
|
return fc;
|
||||||
@@ -148,6 +148,17 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
|
|||||||
fuse_putback_request(fc, req);
|
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(&fuse_lock);
|
||||||
|
list_del(&req->bg_entry);
|
||||||
|
spin_unlock(&fuse_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is called when a request is finished. Either a reply
|
* This function is called when a request is finished. Either a reply
|
||||||
* has arrived or it was interrupted (and not yet sent) or some error
|
* has arrived or it was interrupted (and not yet sent) or some error
|
||||||
@@ -166,12 +177,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
|
|||||||
putback = atomic_dec_and_test(&req->count);
|
putback = atomic_dec_and_test(&req->count);
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
if (req->background) {
|
if (req->background) {
|
||||||
if (req->inode)
|
down_read(&fc->sbput_sem);
|
||||||
iput(req->inode);
|
if (fc->mounted)
|
||||||
if (req->inode2)
|
fuse_release_background(req);
|
||||||
iput(req->inode2);
|
up_read(&fc->sbput_sem);
|
||||||
if (req->file)
|
|
||||||
fput(req->file);
|
|
||||||
}
|
}
|
||||||
wake_up(&req->waitq);
|
wake_up(&req->waitq);
|
||||||
if (req->in.h.opcode == FUSE_INIT) {
|
if (req->in.h.opcode == FUSE_INIT) {
|
||||||
@@ -191,11 +200,39 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
|
|||||||
fuse_putback_request(fc, req);
|
fuse_putback_request(fc, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void background_request(struct fuse_req *req)
|
/*
|
||||||
|
* Unfortunately request interruption not just solves the deadlock
|
||||||
|
* problem, it causes problems too. These stem from the fact, that an
|
||||||
|
* interrupted request is continued to be processed in userspace,
|
||||||
|
* while all the locks and object references (inode and file) held
|
||||||
|
* during the operation are released.
|
||||||
|
*
|
||||||
|
* To release the locks is exactly why there's a need to interrupt the
|
||||||
|
* request, so there's not a lot that can be done about this, except
|
||||||
|
* introduce additional locking in userspace.
|
||||||
|
*
|
||||||
|
* More important is to keep inode and file references until userspace
|
||||||
|
* has replied, otherwise FORGET and RELEASE could be sent while the
|
||||||
|
* inode/file is still used by the filesystem.
|
||||||
|
*
|
||||||
|
* For this reason the concept of "background" request is introduced.
|
||||||
|
* An interrupted request is backgrounded if it has been already sent
|
||||||
|
* to userspace. Backgrounding involves getting an extra reference to
|
||||||
|
* inode(s) or file used in the request, and adding the request to
|
||||||
|
* fc->background list. When a reply is received for a background
|
||||||
|
* request, the object references are released, and the request is
|
||||||
|
* removed from the list. If the filesystem is unmounted while there
|
||||||
|
* are still background requests, the list is walked and references
|
||||||
|
* are released as if a reply was received.
|
||||||
|
*
|
||||||
|
* There's one more use for a background request. The RELEASE message is
|
||||||
|
* always sent as background, since it doesn't return an error or
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
static void background_request(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
/* Need to get hold of the inode(s) and/or file used in the
|
|
||||||
request, so FORGET and RELEASE are not sent too early */
|
|
||||||
req->background = 1;
|
req->background = 1;
|
||||||
|
list_add(&req->bg_entry, &fc->background);
|
||||||
if (req->inode)
|
if (req->inode)
|
||||||
req->inode = igrab(req->inode);
|
req->inode = igrab(req->inode);
|
||||||
if (req->inode2)
|
if (req->inode2)
|
||||||
@@ -215,7 +252,8 @@ static int request_wait_answer_nonint(struct fuse_req *req)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Called with fuse_lock held. Releases, and then reacquires it. */
|
/* Called with fuse_lock held. Releases, and then reacquires it. */
|
||||||
static void request_wait_answer(struct fuse_req *req, int interruptible)
|
static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req,
|
||||||
|
int interruptible)
|
||||||
{
|
{
|
||||||
int intr;
|
int intr;
|
||||||
|
|
||||||
@@ -255,7 +293,7 @@ static void request_wait_answer(struct fuse_req *req, int interruptible)
|
|||||||
list_del(&req->list);
|
list_del(&req->list);
|
||||||
__fuse_put_request(req);
|
__fuse_put_request(req);
|
||||||
} else if (!req->finished && req->sent)
|
} else if (!req->finished && req->sent)
|
||||||
background_request(req);
|
background_request(fc, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned len_args(unsigned numargs, struct fuse_arg *args)
|
static unsigned len_args(unsigned numargs, struct fuse_arg *args)
|
||||||
@@ -297,7 +335,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
|
|||||||
{
|
{
|
||||||
req->isreply = 1;
|
req->isreply = 1;
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
if (!fc->file)
|
if (!fc->connected)
|
||||||
req->out.h.error = -ENOTCONN;
|
req->out.h.error = -ENOTCONN;
|
||||||
else if (fc->conn_error)
|
else if (fc->conn_error)
|
||||||
req->out.h.error = -ECONNREFUSED;
|
req->out.h.error = -ECONNREFUSED;
|
||||||
@@ -307,7 +345,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
|
|||||||
after request_end() */
|
after request_end() */
|
||||||
__fuse_get_request(req);
|
__fuse_get_request(req);
|
||||||
|
|
||||||
request_wait_answer(req, interruptible);
|
request_wait_answer(fc, req, interruptible);
|
||||||
}
|
}
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
}
|
}
|
||||||
@@ -330,7 +368,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
|
|||||||
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
|
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
if (fc->file) {
|
if (fc->connected) {
|
||||||
queue_request(fc, req);
|
queue_request(fc, req);
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
} else {
|
} else {
|
||||||
@@ -348,7 +386,9 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
|
|||||||
void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
|
void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
|
||||||
{
|
{
|
||||||
req->isreply = 1;
|
req->isreply = 1;
|
||||||
background_request(req);
|
spin_lock(&fuse_lock);
|
||||||
|
background_request(fc, req);
|
||||||
|
spin_unlock(&fuse_lock);
|
||||||
request_send_nowait(fc, req);
|
request_send_nowait(fc, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,7 +623,7 @@ static void request_wait(struct fuse_conn *fc)
|
|||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
|
|
||||||
add_wait_queue_exclusive(&fc->waitq, &wait);
|
add_wait_queue_exclusive(&fc->waitq, &wait);
|
||||||
while (fc->sb && list_empty(&fc->pending)) {
|
while (fc->mounted && list_empty(&fc->pending)) {
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
if (signal_pending(current))
|
if (signal_pending(current))
|
||||||
break;
|
break;
|
||||||
@@ -622,7 +662,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
|
|||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
request_wait(fc);
|
request_wait(fc);
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
if (!fc->sb)
|
if (!fc->mounted)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
err = -ERESTARTSYS;
|
err = -ERESTARTSYS;
|
||||||
if (list_empty(&fc->pending))
|
if (list_empty(&fc->pending))
|
||||||
@@ -839,7 +879,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
|
|||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
fc = file->private_data;
|
fc = file->private_data;
|
||||||
if (fc) {
|
if (fc) {
|
||||||
fc->file = NULL;
|
fc->connected = 0;
|
||||||
end_requests(fc, &fc->pending);
|
end_requests(fc, &fc->pending);
|
||||||
end_requests(fc, &fc->processing);
|
end_requests(fc, &fc->processing);
|
||||||
fuse_release_conn(fc);
|
fuse_release_conn(fc);
|
||||||
|
@@ -418,7 +418,8 @@ static int fuse_revalidate(struct dentry *entry)
|
|||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
if (get_node_id(inode) == FUSE_ROOT_ID) {
|
if (get_node_id(inode) == FUSE_ROOT_ID) {
|
||||||
if (current->fsuid != fc->user_id)
|
if (!(fc->flags & FUSE_ALLOW_OTHER) &&
|
||||||
|
current->fsuid != fc->user_id)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
} else if (time_before_eq(jiffies, fi->i_time))
|
} else if (time_before_eq(jiffies, fi->i_time))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -430,9 +431,31 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
|
|||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
if (current->fsuid != fc->user_id)
|
if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id)
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
else {
|
else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
|
||||||
|
int err = generic_permission(inode, mask, NULL);
|
||||||
|
|
||||||
|
/* If permission is denied, try to refresh file
|
||||||
|
attributes. This is also needed, because the root
|
||||||
|
node will at first have no permissions */
|
||||||
|
if (err == -EACCES) {
|
||||||
|
err = fuse_do_getattr(inode);
|
||||||
|
if (!err)
|
||||||
|
err = generic_permission(inode, mask, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Need some mechanism to revoke permissions:
|
||||||
|
currently if the filesystem suddenly changes the
|
||||||
|
file mode, we will not be informed about it, and
|
||||||
|
continue to allow access to the file/directory.
|
||||||
|
|
||||||
|
This is actually not so grave, since the user can
|
||||||
|
simply keep access to the file/directory anyway by
|
||||||
|
keeping it open... */
|
||||||
|
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
int mode = inode->i_mode;
|
int mode = inode->i_mode;
|
||||||
if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
|
if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
|
||||||
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
|
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
|
||||||
@@ -636,6 +659,12 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
|
|||||||
int err;
|
int err;
|
||||||
int is_truncate = 0;
|
int is_truncate = 0;
|
||||||
|
|
||||||
|
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
|
||||||
|
err = inode_change_ok(inode, attr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (attr->ia_valid & ATTR_SIZE) {
|
if (attr->ia_valid & ATTR_SIZE) {
|
||||||
unsigned long limit;
|
unsigned long limit;
|
||||||
is_truncate = 1;
|
is_truncate = 1;
|
||||||
|
@@ -70,7 +70,7 @@ static int fuse_open(struct inode *inode, struct file *file)
|
|||||||
else
|
else
|
||||||
request_send(fc, req);
|
request_send(fc, req);
|
||||||
err = req->out.h.error;
|
err = req->out.h.error;
|
||||||
if (!err)
|
if (!err && !(fc->flags & FUSE_KERNEL_CACHE))
|
||||||
invalidate_inode_pages(inode->i_mapping);
|
invalidate_inode_pages(inode->i_mapping);
|
||||||
if (err) {
|
if (err) {
|
||||||
fuse_request_free(ff->release_req);
|
fuse_request_free(ff->release_req);
|
||||||
|
@@ -21,6 +21,19 @@
|
|||||||
/** If more requests are outstanding, then the operation will block */
|
/** If more requests are outstanding, then the operation will block */
|
||||||
#define FUSE_MAX_OUTSTANDING 10
|
#define FUSE_MAX_OUTSTANDING 10
|
||||||
|
|
||||||
|
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
|
||||||
|
module will check permissions based on the file mode. Otherwise no
|
||||||
|
permission checking is done in the kernel */
|
||||||
|
#define FUSE_DEFAULT_PERMISSIONS (1 << 0)
|
||||||
|
|
||||||
|
/** If the FUSE_ALLOW_OTHER flag is given, then not only the user
|
||||||
|
doing the mount will be allowed to access the filesystem */
|
||||||
|
#define FUSE_ALLOW_OTHER (1 << 1)
|
||||||
|
|
||||||
|
/** If the FUSE_KERNEL_CACHE flag is given, then cached data will not
|
||||||
|
be flushed on open */
|
||||||
|
#define FUSE_KERNEL_CACHE (1 << 2)
|
||||||
|
|
||||||
/** FUSE inode */
|
/** FUSE inode */
|
||||||
struct fuse_inode {
|
struct fuse_inode {
|
||||||
/** Inode data */
|
/** Inode data */
|
||||||
@@ -109,6 +122,9 @@ struct fuse_req {
|
|||||||
lists in fuse_conn */
|
lists in fuse_conn */
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|
||||||
|
/** Entry on the background list */
|
||||||
|
struct list_head bg_entry;
|
||||||
|
|
||||||
/** refcount */
|
/** refcount */
|
||||||
atomic_t count;
|
atomic_t count;
|
||||||
|
|
||||||
@@ -176,15 +192,15 @@ struct fuse_req {
|
|||||||
* unmounted.
|
* unmounted.
|
||||||
*/
|
*/
|
||||||
struct fuse_conn {
|
struct fuse_conn {
|
||||||
/** The superblock of the mounted filesystem */
|
/** Reference count */
|
||||||
struct super_block *sb;
|
int count;
|
||||||
|
|
||||||
/** The opened client device */
|
|
||||||
struct file *file;
|
|
||||||
|
|
||||||
/** The user id for this mount */
|
/** The user id for this mount */
|
||||||
uid_t user_id;
|
uid_t user_id;
|
||||||
|
|
||||||
|
/** The fuse mount flags for this mount */
|
||||||
|
unsigned flags;
|
||||||
|
|
||||||
/** Readers of the connection are waiting on this */
|
/** Readers of the connection are waiting on this */
|
||||||
wait_queue_head_t waitq;
|
wait_queue_head_t waitq;
|
||||||
|
|
||||||
@@ -194,6 +210,10 @@ struct fuse_conn {
|
|||||||
/** The list of requests being processed */
|
/** The list of requests being processed */
|
||||||
struct list_head processing;
|
struct list_head processing;
|
||||||
|
|
||||||
|
/** Requests put in the background (RELEASE or any other
|
||||||
|
interrupted request) */
|
||||||
|
struct list_head background;
|
||||||
|
|
||||||
/** Controls the maximum number of outstanding requests */
|
/** Controls the maximum number of outstanding requests */
|
||||||
struct semaphore outstanding_sem;
|
struct semaphore outstanding_sem;
|
||||||
|
|
||||||
@@ -201,12 +221,21 @@ struct fuse_conn {
|
|||||||
outstanding_sem would go negative */
|
outstanding_sem would go negative */
|
||||||
unsigned outstanding_debt;
|
unsigned outstanding_debt;
|
||||||
|
|
||||||
|
/** RW semaphore for exclusion with fuse_put_super() */
|
||||||
|
struct rw_semaphore sbput_sem;
|
||||||
|
|
||||||
/** The list of unused requests */
|
/** The list of unused requests */
|
||||||
struct list_head unused_list;
|
struct list_head unused_list;
|
||||||
|
|
||||||
/** The next unique request id */
|
/** The next unique request id */
|
||||||
u64 reqctr;
|
u64 reqctr;
|
||||||
|
|
||||||
|
/** Mount is active */
|
||||||
|
unsigned mounted : 1;
|
||||||
|
|
||||||
|
/** Connection established */
|
||||||
|
unsigned connected : 1;
|
||||||
|
|
||||||
/** Connection failed (version mismatch) */
|
/** Connection failed (version mismatch) */
|
||||||
unsigned conn_error : 1;
|
unsigned conn_error : 1;
|
||||||
|
|
||||||
@@ -261,6 +290,7 @@ extern struct file_operations fuse_dev_operations;
|
|||||||
* - the private_data field of the device file
|
* - the private_data field of the device file
|
||||||
* - the s_fs_info field of the super block
|
* - the s_fs_info field of the super block
|
||||||
* - unused_list, pending, processing lists in fuse_conn
|
* - unused_list, pending, processing lists in fuse_conn
|
||||||
|
* - background list in fuse_conn
|
||||||
* - the unique request ID counter reqctr in fuse_conn
|
* - the unique request ID counter reqctr in fuse_conn
|
||||||
* - the sb (super_block) field in fuse_conn
|
* - the sb (super_block) field in fuse_conn
|
||||||
* - the file (device file) field in fuse_conn
|
* - the file (device file) field in fuse_conn
|
||||||
@@ -371,6 +401,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
|
|||||||
*/
|
*/
|
||||||
void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
|
void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release inodes and file assiciated with background request
|
||||||
|
*/
|
||||||
|
void fuse_release_background(struct fuse_req *req);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes of a file
|
* Get the attributes of a file
|
||||||
*/
|
*/
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/moduleparam.h>
|
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
#include <linux/statfs.h>
|
#include <linux/statfs.h>
|
||||||
|
|
||||||
@@ -25,11 +24,6 @@ MODULE_LICENSE("GPL");
|
|||||||
|
|
||||||
spinlock_t fuse_lock;
|
spinlock_t fuse_lock;
|
||||||
static kmem_cache_t *fuse_inode_cachep;
|
static kmem_cache_t *fuse_inode_cachep;
|
||||||
static int mount_count;
|
|
||||||
|
|
||||||
static int mount_max = 1000;
|
|
||||||
module_param(mount_max, int, 0644);
|
|
||||||
MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)");
|
|
||||||
|
|
||||||
#define FUSE_SUPER_MAGIC 0x65735546
|
#define FUSE_SUPER_MAGIC 0x65735546
|
||||||
|
|
||||||
@@ -37,6 +31,7 @@ struct fuse_mount_data {
|
|||||||
int fd;
|
int fd;
|
||||||
unsigned rootmode;
|
unsigned rootmode;
|
||||||
unsigned user_id;
|
unsigned user_id;
|
||||||
|
unsigned flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct inode *fuse_alloc_inode(struct super_block *sb)
|
static struct inode *fuse_alloc_inode(struct super_block *sb)
|
||||||
@@ -89,8 +84,8 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
|
|||||||
|
|
||||||
static void fuse_clear_inode(struct inode *inode)
|
static void fuse_clear_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
if (inode->i_sb->s_flags & MS_ACTIVE) {
|
||||||
if (fc) {
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
|
fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
|
||||||
fi->forget_req = NULL;
|
fi->forget_req = NULL;
|
||||||
@@ -195,14 +190,19 @@ static void fuse_put_super(struct super_block *sb)
|
|||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||||
|
|
||||||
|
down_write(&fc->sbput_sem);
|
||||||
|
while (!list_empty(&fc->background))
|
||||||
|
fuse_release_background(list_entry(fc->background.next,
|
||||||
|
struct fuse_req, bg_entry));
|
||||||
|
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
mount_count --;
|
fc->mounted = 0;
|
||||||
fc->sb = NULL;
|
|
||||||
fc->user_id = 0;
|
fc->user_id = 0;
|
||||||
|
fc->flags = 0;
|
||||||
/* Flush all readers on this fs */
|
/* Flush all readers on this fs */
|
||||||
wake_up_all(&fc->waitq);
|
wake_up_all(&fc->waitq);
|
||||||
|
up_write(&fc->sbput_sem);
|
||||||
fuse_release_conn(fc);
|
fuse_release_conn(fc);
|
||||||
*get_fuse_conn_super_p(sb) = NULL;
|
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,6 @@ enum {
|
|||||||
OPT_USER_ID,
|
OPT_USER_ID,
|
||||||
OPT_DEFAULT_PERMISSIONS,
|
OPT_DEFAULT_PERMISSIONS,
|
||||||
OPT_ALLOW_OTHER,
|
OPT_ALLOW_OTHER,
|
||||||
OPT_ALLOW_ROOT,
|
|
||||||
OPT_KERNEL_CACHE,
|
OPT_KERNEL_CACHE,
|
||||||
OPT_ERR
|
OPT_ERR
|
||||||
};
|
};
|
||||||
@@ -260,7 +259,6 @@ static match_table_t tokens = {
|
|||||||
{OPT_USER_ID, "user_id=%u"},
|
{OPT_USER_ID, "user_id=%u"},
|
||||||
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
|
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
|
||||||
{OPT_ALLOW_OTHER, "allow_other"},
|
{OPT_ALLOW_OTHER, "allow_other"},
|
||||||
{OPT_ALLOW_ROOT, "allow_root"},
|
|
||||||
{OPT_KERNEL_CACHE, "kernel_cache"},
|
{OPT_KERNEL_CACHE, "kernel_cache"},
|
||||||
{OPT_ERR, NULL}
|
{OPT_ERR, NULL}
|
||||||
};
|
};
|
||||||
@@ -298,6 +296,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
|
|||||||
d->user_id = value;
|
d->user_id = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPT_DEFAULT_PERMISSIONS:
|
||||||
|
d->flags |= FUSE_DEFAULT_PERMISSIONS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPT_ALLOW_OTHER:
|
||||||
|
d->flags |= FUSE_ALLOW_OTHER;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPT_KERNEL_CACHE:
|
||||||
|
d->flags |= FUSE_KERNEL_CACHE;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -313,6 +323,12 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
|
|||||||
struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb);
|
struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb);
|
||||||
|
|
||||||
seq_printf(m, ",user_id=%u", fc->user_id);
|
seq_printf(m, ",user_id=%u", fc->user_id);
|
||||||
|
if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
|
||||||
|
seq_puts(m, ",default_permissions");
|
||||||
|
if (fc->flags & FUSE_ALLOW_OTHER)
|
||||||
|
seq_puts(m, ",allow_other");
|
||||||
|
if (fc->flags & FUSE_KERNEL_CACHE)
|
||||||
|
seq_puts(m, ",kernel_cache");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +346,8 @@ static void free_conn(struct fuse_conn *fc)
|
|||||||
/* Must be called with the fuse lock held */
|
/* Must be called with the fuse lock held */
|
||||||
void fuse_release_conn(struct fuse_conn *fc)
|
void fuse_release_conn(struct fuse_conn *fc)
|
||||||
{
|
{
|
||||||
if (!fc->sb && !fc->file)
|
fc->count--;
|
||||||
|
if (!fc->count)
|
||||||
free_conn(fc);
|
free_conn(fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,14 +359,13 @@ static struct fuse_conn *new_conn(void)
|
|||||||
if (fc != NULL) {
|
if (fc != NULL) {
|
||||||
int i;
|
int i;
|
||||||
memset(fc, 0, sizeof(*fc));
|
memset(fc, 0, sizeof(*fc));
|
||||||
fc->sb = NULL;
|
|
||||||
fc->file = NULL;
|
|
||||||
fc->user_id = 0;
|
|
||||||
init_waitqueue_head(&fc->waitq);
|
init_waitqueue_head(&fc->waitq);
|
||||||
INIT_LIST_HEAD(&fc->pending);
|
INIT_LIST_HEAD(&fc->pending);
|
||||||
INIT_LIST_HEAD(&fc->processing);
|
INIT_LIST_HEAD(&fc->processing);
|
||||||
INIT_LIST_HEAD(&fc->unused_list);
|
INIT_LIST_HEAD(&fc->unused_list);
|
||||||
|
INIT_LIST_HEAD(&fc->background);
|
||||||
sema_init(&fc->outstanding_sem, 0);
|
sema_init(&fc->outstanding_sem, 0);
|
||||||
|
init_rwsem(&fc->sbput_sem);
|
||||||
for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
|
for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
|
||||||
struct fuse_req *req = fuse_request_alloc();
|
struct fuse_req *req = fuse_request_alloc();
|
||||||
if (!req) {
|
if (!req) {
|
||||||
@@ -380,8 +396,10 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
|
|||||||
fc = ERR_PTR(-EINVAL);
|
fc = ERR_PTR(-EINVAL);
|
||||||
} else {
|
} else {
|
||||||
file->private_data = fc;
|
file->private_data = fc;
|
||||||
fc->sb = sb;
|
*get_fuse_conn_super_p(sb) = fc;
|
||||||
fc->file = file;
|
fc->mounted = 1;
|
||||||
|
fc->connected = 1;
|
||||||
|
fc->count = 2;
|
||||||
}
|
}
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
return fc;
|
return fc;
|
||||||
@@ -407,17 +425,6 @@ static struct super_operations fuse_super_operations = {
|
|||||||
.show_options = fuse_show_options,
|
.show_options = fuse_show_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int inc_mount_count(void)
|
|
||||||
{
|
|
||||||
int success = 0;
|
|
||||||
spin_lock(&fuse_lock);
|
|
||||||
mount_count ++;
|
|
||||||
if (mount_max == -1 || mount_count <= mount_max)
|
|
||||||
success = 1;
|
|
||||||
spin_unlock(&fuse_lock);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc;
|
||||||
@@ -444,14 +451,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
if (IS_ERR(fc))
|
if (IS_ERR(fc))
|
||||||
return PTR_ERR(fc);
|
return PTR_ERR(fc);
|
||||||
|
|
||||||
|
fc->flags = d.flags;
|
||||||
fc->user_id = d.user_id;
|
fc->user_id = d.user_id;
|
||||||
|
|
||||||
*get_fuse_conn_super_p(sb) = fc;
|
|
||||||
|
|
||||||
err = -ENFILE;
|
|
||||||
if (!inc_mount_count() && current->uid != 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
root = get_root_inode(sb, d.rootmode);
|
root = get_root_inode(sb, d.rootmode);
|
||||||
if (root == NULL)
|
if (root == NULL)
|
||||||
@@ -467,11 +469,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
|
|
||||||
err:
|
err:
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
mount_count --;
|
|
||||||
fc->sb = NULL;
|
|
||||||
fuse_release_conn(fc);
|
fuse_release_conn(fc);
|
||||||
spin_unlock(&fuse_lock);
|
spin_unlock(&fuse_lock);
|
||||||
*get_fuse_conn_super_p(sb) = NULL;
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user