blob: 8c7c7d6a15b6f24ea4b47da9ebb16402afbcaf85 [file] [log] [blame]
Miklos Szeredibc22e7b2001-10-23 19:26:04 +00001/*
Miklos Szeredie56818b2004-12-12 11:45:24 +00002 FUSE: Filesystem in Userspace
Miklos Szeredi149f6072005-01-10 12:29:28 +00003 Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
Miklos Szeredibc22e7b2001-10-23 19:26:04 +00004
Miklos Szeredie56818b2004-12-12 11:45:24 +00005 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
Miklos Szeredibc22e7b2001-10-23 19:26:04 +00007*/
8
Miklos Szeredibc22e7b2001-10-23 19:26:04 +00009#include "fuse_i.h"
10
Miklos Szeredi05033042001-11-13 16:11:35 +000011#include <linux/pagemap.h>
Miklos Szeredi85c74fc2001-10-28 19:44:14 +000012#include <linux/slab.h>
Miklos Szeredibc22e7b2001-10-23 19:26:04 +000013#include <linux/file.h>
Miklos Szeredi7f6bc892004-04-13 10:49:14 +000014#include <linux/mount.h>
Miklos Szeredi7f6bc892004-04-13 10:49:14 +000015#include <linux/seq_file.h>
Miklos Szeredic6ee9fd2005-01-10 09:53:04 +000016#include <linux/init.h>
Miklos Szeredi13ed4822004-11-20 11:12:21 +000017#include <linux/module.h>
Miklos Szeredif85ab242004-01-07 12:16:45 +000018#ifdef KERNEL_2_6
Miklos Szeredi8ec48ec2004-04-19 10:24:41 +000019#include <linux/parser.h>
Miklos Szeredif85ab242004-01-07 12:16:45 +000020#include <linux/statfs.h>
Miklos Szeredi8ec48ec2004-04-19 10:24:41 +000021#else
22#include "compat/parser.h"
Miklos Szeredif85ab242004-01-07 12:16:45 +000023#endif
Miklos Szeredibc22e7b2001-10-23 19:26:04 +000024
Miklos Szeredic6ee9fd2005-01-10 09:53:04 +000025MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
26MODULE_DESCRIPTION("Filesystem in Userspace");
27#ifdef MODULE_LICENSE
28MODULE_LICENSE("GPL");
29#endif
30
31spinlock_t fuse_lock;
Miklos Szeredi069c9502004-07-16 16:17:02 +000032static kmem_cache_t *fuse_inode_cachep;
Miklos Szerediacb4d362004-07-02 16:20:45 +000033
Miklos Szeredibc22e7b2001-10-23 19:26:04 +000034#define FUSE_SUPER_MAGIC 0x65735546
35
Miklos Szeredif85ab242004-01-07 12:16:45 +000036#ifndef KERNEL_2_6
37#define kstatfs statfs
38#endif
Miklos Szeredib1ab8b52004-11-01 10:57:41 +000039#ifndef MAX_LFS_FILESIZE
Miklos Szerediaa63b6b2004-12-03 13:24:35 +000040#define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)
Miklos Szeredib1ab8b52004-11-01 10:57:41 +000041#endif
Miklos Szeredi7f6bc892004-04-13 10:49:14 +000042struct fuse_mount_data {
43 int fd;
Miklos Szeredi83a07442004-11-30 18:25:20 +000044 unsigned rootmode;
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +000045 unsigned user_id;
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +000046 unsigned group_id;
Miklos Szeredi437d8112005-07-06 09:14:20 +000047 unsigned fd_present : 1;
48 unsigned rootmode_present : 1;
49 unsigned user_id_present : 1;
50 unsigned group_id_present : 1;
Miklos Szeredi83a07442004-11-30 18:25:20 +000051 unsigned flags;
52 unsigned max_read;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +000053};
Miklos Szeredi69cc79a2004-02-17 08:57:29 +000054
Miklos Szeredia13d9002004-11-02 17:32:03 +000055static struct inode *fuse_alloc_inode(struct super_block *sb)
Miklos Szeredi069c9502004-07-16 16:17:02 +000056{
Miklos Szeredia13d9002004-11-02 17:32:03 +000057 struct inode *inode;
Miklos Szeredi069c9502004-07-16 16:17:02 +000058 struct fuse_inode *fi;
59
Miklos Szeredia13d9002004-11-02 17:32:03 +000060 inode = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL);
61 if (!inode)
62 return NULL;
Miklos Szeredi069c9502004-07-16 16:17:02 +000063
Miklos Szeredia13d9002004-11-02 17:32:03 +000064#ifndef KERNEL_2_6
65 inode->u.generic_ip = NULL;
66#endif
Miklos Szeredi039322d2004-12-01 18:39:12 +000067 fi = get_fuse_inode(inode);
Miklos Szeredi81394522005-01-11 14:24:18 +000068 fi->i_time = jiffies - 1;
69 fi->nodeid = 0;
Miklos Szeredi38009022005-05-08 19:47:22 +000070 fi->nlookup = 0;
Miklos Szeredia13d9002004-11-02 17:32:03 +000071 fi->forget_req = fuse_request_alloc();
72 if (!fi->forget_req) {
73 kmem_cache_free(fuse_inode_cachep, inode);
74 return NULL;
75 }
Miklos Szeredia13d9002004-11-02 17:32:03 +000076
77 return inode;
Miklos Szeredi069c9502004-07-16 16:17:02 +000078}
79
Miklos Szeredia13d9002004-11-02 17:32:03 +000080static void fuse_destroy_inode(struct inode *inode)
Miklos Szeredi069c9502004-07-16 16:17:02 +000081{
Miklos Szeredi039322d2004-12-01 18:39:12 +000082 struct fuse_inode *fi = get_fuse_inode(inode);
Miklos Szeredi209f5d02004-07-24 19:56:16 +000083 if (fi->forget_req)
84 fuse_request_free(fi->forget_req);
Miklos Szeredia13d9002004-11-02 17:32:03 +000085 kmem_cache_free(fuse_inode_cachep, inode);
Miklos Szeredi069c9502004-07-16 16:17:02 +000086}
87
Miklos Szeredibc22e7b2001-10-23 19:26:04 +000088static void fuse_read_inode(struct inode *inode)
89{
Miklos Szeredi90d8bef2001-10-26 14:55:42 +000090 /* No op */
Miklos Szeredibc22e7b2001-10-23 19:26:04 +000091}
92
Miklos Szeredia13d9002004-11-02 17:32:03 +000093void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
Miklos Szeredi38009022005-05-08 19:47:22 +000094 unsigned long nodeid, u64 nlookup)
Miklos Szeredi2778f6c2004-06-21 09:45:30 +000095{
96 struct fuse_forget_in *inarg = &req->misc.forget_in;
Miklos Szeredi38009022005-05-08 19:47:22 +000097 inarg->nlookup = nlookup;
Miklos Szeredi2778f6c2004-06-21 09:45:30 +000098 req->in.h.opcode = FUSE_FORGET;
Miklos Szeredia13d9002004-11-02 17:32:03 +000099 req->in.h.nodeid = nodeid;
Miklos Szeredi2778f6c2004-06-21 09:45:30 +0000100 req->in.numargs = 1;
101 req->in.args[0].size = sizeof(struct fuse_forget_in);
102 req->in.args[0].value = inarg;
103 request_send_noreply(fc, req);
104}
105
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000106static void fuse_clear_inode(struct inode *inode)
107{
Miklos Szeredi0111f9d2005-04-22 12:04:55 +0000108 if (inode->i_sb->s_flags & MS_ACTIVE) {
109 struct fuse_conn *fc = get_fuse_conn(inode);
Miklos Szeredi039322d2004-12-01 18:39:12 +0000110 struct fuse_inode *fi = get_fuse_inode(inode);
Miklos Szeredi38009022005-05-08 19:47:22 +0000111 fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
Miklos Szeredia13d9002004-11-02 17:32:03 +0000112 fi->forget_req = NULL;
Miklos Szeredid3dd2d52004-06-22 18:46:02 +0000113 }
Miklos Szeredi85c74fc2001-10-28 19:44:14 +0000114}
115
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000116void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
117{
118 if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
119#ifdef KERNEL_2_6
120 invalidate_inode_pages(inode->i_mapping);
121#else
122 invalidate_inode_pages(inode);
123#endif
124
125 inode->i_ino = attr->ino;
126 inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
127 inode->i_nlink = attr->nlink;
128 inode->i_uid = attr->uid;
129 inode->i_gid = attr->gid;
130 i_size_write(inode, attr->size);
131 inode->i_blksize = PAGE_CACHE_SIZE;
132 inode->i_blocks = attr->blocks;
133#ifdef KERNEL_2_6
134 inode->i_atime.tv_sec = attr->atime;
135 inode->i_atime.tv_nsec = attr->atimensec;
136 inode->i_mtime.tv_sec = attr->mtime;
137 inode->i_mtime.tv_nsec = attr->mtimensec;
138 inode->i_ctime.tv_sec = attr->ctime;
139 inode->i_ctime.tv_nsec = attr->ctimensec;
140#else
141 inode->i_atime = attr->atime;
142 inode->i_mtime = attr->mtime;
143 inode->i_ctime = attr->ctime;
144#endif
145}
146
147static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
148{
149 inode->i_mode = attr->mode & S_IFMT;
150 i_size_write(inode, attr->size);
151 if (S_ISREG(inode->i_mode)) {
152 fuse_init_common(inode);
153 fuse_init_file_inode(inode);
154 } else if (S_ISDIR(inode->i_mode))
155 fuse_init_dir(inode);
156 else if (S_ISLNK(inode->i_mode))
157 fuse_init_symlink(inode);
158 else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
159 S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
160 fuse_init_common(inode);
161 init_special_inode(inode, inode->i_mode,
162 new_decode_dev(attr->rdev));
163 } else {
164 /* Don't let user create weird files */
165 inode->i_mode = S_IFREG;
166 fuse_init_common(inode);
167 fuse_init_file_inode(inode);
168 }
169}
170
171#ifdef KERNEL_2_6
172static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
173{
174 unsigned long nodeid = *(unsigned long *) _nodeidp;
175 if (get_node_id(inode) == nodeid)
176 return 1;
177 else
178 return 0;
179}
180
181static int fuse_inode_set(struct inode *inode, void *_nodeidp)
182{
183 unsigned long nodeid = *(unsigned long *) _nodeidp;
184 get_fuse_inode(inode)->nodeid = nodeid;
185 return 0;
186}
187
188struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
Miklos Szeredi38009022005-05-08 19:47:22 +0000189 int generation, struct fuse_attr *attr)
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000190{
191 struct inode *inode;
Miklos Szeredi38009022005-05-08 19:47:22 +0000192 struct fuse_inode *fi;
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000193 struct fuse_conn *fc = get_fuse_conn_super(sb);
194 int retried = 0;
195
196 retry:
197 inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
198 if (!inode)
199 return NULL;
200
201 if ((inode->i_state & I_NEW)) {
Miklos Szeredi3b9e53f2005-09-02 16:04:48 +0000202#ifdef KERNEL_2_6_8_PLUS
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000203 inode->i_flags |= S_NOATIME|S_NOCMTIME;
Miklos Szeredi3b9e53f2005-09-02 16:04:48 +0000204#else
205 inode->i_flags |= S_NOATIME;
206#endif
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000207 inode->i_generation = generation;
208 inode->i_data.backing_dev_info = &fc->bdi;
209 fuse_init_inode(inode, attr);
210 unlock_new_inode(inode);
211 } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
212 BUG_ON(retried);
213 /* Inode has changed type, any I/O on the old should fail */
214 make_bad_inode(inode);
215 iput(inode);
216 retried = 1;
217 goto retry;
218 }
219
Miklos Szeredi38009022005-05-08 19:47:22 +0000220 fi = get_fuse_inode(inode);
221 fi->nlookup ++;
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000222 fuse_change_attributes(inode, attr);
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000223 return inode;
224}
225#else
226static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){
227 unsigned long nodeid = *(unsigned long *) _nodeidp;
228 if (inode->u.generic_ip && get_node_id(inode) == nodeid)
229 return 1;
230 else
231 return 0;
232}
233
234struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
Miklos Szeredi38009022005-05-08 19:47:22 +0000235 int generation, struct fuse_attr *attr)
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000236{
237 struct inode *inode;
Miklos Szeredi38009022005-05-08 19:47:22 +0000238 struct fuse_inode *fi;
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000239 int retried = 0;
240
241 retry:
242 inode = iget4(sb, attr->ino, fuse_inode_eq, &nodeid);
243 if (!inode)
244 return NULL;
245
246 if (!inode->u.generic_ip) {
247 get_fuse_inode(inode)->nodeid = nodeid;
248 inode->u.generic_ip = inode;
249 inode->i_generation = generation;
250 fuse_init_inode(inode, attr);
251 } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
252 BUG_ON(retried);
253 /* Inode has changed type, any I/O on the old should fail */
254 remove_inode_hash(inode);
255 make_bad_inode(inode);
256 iput(inode);
257 retried = 1;
258 goto retry;
259 }
260
Miklos Szeredi38009022005-05-08 19:47:22 +0000261 fi = get_fuse_inode(inode);
262 fi->nlookup ++;
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000263 fuse_change_attributes(inode, attr);
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000264 return inode;
265}
266#endif
267
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000268static void fuse_put_super(struct super_block *sb)
269{
Miklos Szeredi039322d2004-12-01 18:39:12 +0000270 struct fuse_conn *fc = get_fuse_conn_super(sb);
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000271
Miklos Szeredid17da462005-03-21 11:47:04 +0000272 down_write(&fc->sbput_sem);
273 while (!list_empty(&fc->background))
274 fuse_release_background(list_entry(fc->background.next,
275 struct fuse_req, bg_entry));
276
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000277 spin_lock(&fuse_lock);
Miklos Szeredi0111f9d2005-04-22 12:04:55 +0000278 fc->mounted = 0;
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000279 fc->user_id = 0;
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000280 fc->group_id = 0;
Miklos Szeredi8cffdb92001-11-09 14:49:18 +0000281 fc->flags = 0;
Miklos Szeredi96249982001-11-21 12:21:19 +0000282 /* Flush all readers on this fs */
283 wake_up_all(&fc->waitq);
Miklos Szeredid17da462005-03-21 11:47:04 +0000284 up_write(&fc->sbput_sem);
Miklos Szeredi79b461a2003-03-10 09:35:34 +0000285 fuse_release_conn(fc);
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000286 spin_unlock(&fuse_lock);
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000287}
288
Miklos Szeredif85ab242004-01-07 12:16:45 +0000289static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
Mark Glinesd84b39a2002-01-07 16:32:02 +0000290{
291 stbuf->f_type = FUSE_SUPER_MAGIC;
Miklos Szeredi18e75e42004-02-19 14:23:27 +0000292 stbuf->f_bsize = attr->bsize;
Miklos Szeredibb4b9742005-11-29 11:29:44 +0000293#ifdef KERNEL_2_6
Miklos Szeredi2b478112005-11-28 13:27:10 +0000294 stbuf->f_frsize = attr->frsize;
Miklos Szeredibb4b9742005-11-29 11:29:44 +0000295#endif
Mark Glinesd84b39a2002-01-07 16:32:02 +0000296 stbuf->f_blocks = attr->blocks;
Miklos Szeredi18e75e42004-02-19 14:23:27 +0000297 stbuf->f_bfree = attr->bfree;
298 stbuf->f_bavail = attr->bavail;
Mark Glinesd84b39a2002-01-07 16:32:02 +0000299 stbuf->f_files = attr->files;
Miklos Szeredi18e75e42004-02-19 14:23:27 +0000300 stbuf->f_ffree = attr->ffree;
Mark Glinesd84b39a2002-01-07 16:32:02 +0000301 stbuf->f_namelen = attr->namelen;
Miklos Szeredi18e75e42004-02-19 14:23:27 +0000302 /* fsid is left zero */
Mark Glinesd84b39a2002-01-07 16:32:02 +0000303}
304
Miklos Szeredif85ab242004-01-07 12:16:45 +0000305static int fuse_statfs(struct super_block *sb, struct kstatfs *buf)
Mark Glinesd84b39a2002-01-07 16:32:02 +0000306{
Miklos Szeredi039322d2004-12-01 18:39:12 +0000307 struct fuse_conn *fc = get_fuse_conn_super(sb);
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000308 struct fuse_req *req;
Mark Glinesd84b39a2002-01-07 16:32:02 +0000309 struct fuse_statfs_out outarg;
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000310 int err;
311
312 req = fuse_get_request(fc);
313 if (!req)
Miklos Szeredif94e0102005-05-12 14:56:34 +0000314 return -EINTR;
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000315
Miklos Szeredi2b478112005-11-28 13:27:10 +0000316 memset(&outarg, 0, sizeof(outarg));
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000317 req->in.numargs = 0;
318 req->in.h.opcode = FUSE_STATFS;
319 req->out.numargs = 1;
Miklos Szerediead7f102005-11-28 16:02:27 +0000320 req->out.args[0].size =
Miklos Szeredi2b478112005-11-28 13:27:10 +0000321 fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg);
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000322 req->out.args[0].value = &outarg;
323 request_send(fc, req);
324 err = req->out.h.error;
325 if (!err)
Miklos Szeredif85ab242004-01-07 12:16:45 +0000326 convert_fuse_statfs(buf, &outarg.st);
Miklos Szeredi7eafcce2004-06-19 22:42:38 +0000327 fuse_put_request(fc, req);
328 return err;
Mark Glinesd84b39a2002-01-07 16:32:02 +0000329}
330
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000331enum {
332 OPT_FD,
333 OPT_ROOTMODE,
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000334 OPT_USER_ID,
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000335 OPT_GROUP_ID,
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000336 OPT_DEFAULT_PERMISSIONS,
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000337 OPT_ALLOW_OTHER,
Miklos Szeredie56818b2004-12-12 11:45:24 +0000338#ifndef KERNEL_2_6
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000339 OPT_LARGE_READ,
Miklos Szeredia25d4c22004-11-23 22:32:16 +0000340#endif
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000341 OPT_MAX_READ,
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000342 OPT_ERR
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000343};
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000344
345static match_table_t tokens = {
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000346 {OPT_FD, "fd=%u"},
347 {OPT_ROOTMODE, "rootmode=%o"},
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000348 {OPT_USER_ID, "user_id=%u"},
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000349 {OPT_GROUP_ID, "group_id=%u"},
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000350 {OPT_DEFAULT_PERMISSIONS, "default_permissions"},
351 {OPT_ALLOW_OTHER, "allow_other"},
Miklos Szeredie56818b2004-12-12 11:45:24 +0000352#ifndef KERNEL_2_6
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000353 {OPT_LARGE_READ, "large_read"},
Miklos Szeredia25d4c22004-11-23 22:32:16 +0000354#endif
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000355 {OPT_MAX_READ, "max_read=%u"},
356 {OPT_ERR, NULL}
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000357};
358
359static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
360{
361 char *p;
362 memset(d, 0, sizeof(struct fuse_mount_data));
Miklos Szerediad051c32004-07-02 09:22:50 +0000363 d->max_read = ~0;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000364
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000365 while ((p = strsep(&opt, ",")) != NULL) {
366 int token;
367 int value;
368 substring_t args[MAX_OPT_ARGS];
369 if (!*p)
370 continue;
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000371
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000372 token = match_token(p, tokens, args);
373 switch (token) {
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000374 case OPT_FD:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000375 if (match_int(&args[0], &value))
376 return 0;
377 d->fd = value;
Miklos Szeredi437d8112005-07-06 09:14:20 +0000378 d->fd_present = 1;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000379 break;
380
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000381 case OPT_ROOTMODE:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000382 if (match_octal(&args[0], &value))
383 return 0;
384 d->rootmode = value;
Miklos Szeredi437d8112005-07-06 09:14:20 +0000385 d->rootmode_present = 1;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000386 break;
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000387
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000388 case OPT_USER_ID:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000389 if (match_int(&args[0], &value))
390 return 0;
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000391 d->user_id = value;
Miklos Szeredi437d8112005-07-06 09:14:20 +0000392 d->user_id_present = 1;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000393 break;
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000394
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000395 case OPT_GROUP_ID:
396 if (match_int(&args[0], &value))
397 return 0;
398 d->group_id = value;
Miklos Szeredi437d8112005-07-06 09:14:20 +0000399 d->group_id_present = 1;
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000400 break;
401
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000402 case OPT_DEFAULT_PERMISSIONS:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000403 d->flags |= FUSE_DEFAULT_PERMISSIONS;
404 break;
405
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000406 case OPT_ALLOW_OTHER:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000407 d->flags |= FUSE_ALLOW_OTHER;
408 break;
409
Miklos Szeredi1bf53ee2004-09-13 11:47:59 +0000410#ifndef KERNEL_2_6
Miklos Szeredie56818b2004-12-12 11:45:24 +0000411 case OPT_LARGE_READ:
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000412 d->flags |= FUSE_LARGE_READ;
Miklos Szeredi1bf53ee2004-09-13 11:47:59 +0000413 break;
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000414#endif
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000415 case OPT_MAX_READ:
Miklos Szerediad051c32004-07-02 09:22:50 +0000416 if (match_int(&args[0], &value))
417 return 0;
418 d->max_read = value;
419 break;
420
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000421 default:
422 return 0;
423 }
424 }
Miklos Szeredi9b813af2005-07-21 07:59:37 +0000425
Miklos Szeredi437d8112005-07-06 09:14:20 +0000426 if (!d->fd_present || !d->rootmode_present ||
427 !d->user_id_present || !d->group_id_present)
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000428 return 0;
429
430 return 1;
431}
432
433static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
434{
Miklos Szeredi039322d2004-12-01 18:39:12 +0000435 struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb);
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000436
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000437 seq_printf(m, ",user_id=%u", fc->user_id);
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000438 seq_printf(m, ",group_id=%u", fc->group_id);
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000439 if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
440 seq_puts(m, ",default_permissions");
441 if (fc->flags & FUSE_ALLOW_OTHER)
442 seq_puts(m, ",allow_other");
Miklos Szeredi209f5d02004-07-24 19:56:16 +0000443#ifndef KERNEL_2_6
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000444 if (fc->flags & FUSE_LARGE_READ)
445 seq_puts(m, ",large_read");
Miklos Szeredi209f5d02004-07-24 19:56:16 +0000446#endif
Miklos Szerediad051c32004-07-02 09:22:50 +0000447 if (fc->max_read != ~0)
448 seq_printf(m, ",max_read=%u", fc->max_read);
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000449 return 0;
450}
451
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000452static void free_conn(struct fuse_conn *fc)
453{
454 while (!list_empty(&fc->unused_list)) {
455 struct fuse_req *req;
456 req = list_entry(fc->unused_list.next, struct fuse_req, list);
457 list_del(&req->list);
458 fuse_request_free(req);
459 }
460 kfree(fc);
461}
462
463/* Must be called with the fuse lock held */
464void fuse_release_conn(struct fuse_conn *fc)
465{
Miklos Szeredi0111f9d2005-04-22 12:04:55 +0000466 fc->count--;
467 if (!fc->count)
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000468 free_conn(fc);
469}
470
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000471static struct fuse_conn *new_conn(void)
472{
473 struct fuse_conn *fc;
474
475 fc = kmalloc(sizeof(*fc), GFP_KERNEL);
476 if (fc != NULL) {
477 int i;
478 memset(fc, 0, sizeof(*fc));
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000479 init_waitqueue_head(&fc->waitq);
480 INIT_LIST_HEAD(&fc->pending);
481 INIT_LIST_HEAD(&fc->processing);
482 INIT_LIST_HEAD(&fc->unused_list);
Miklos Szeredid17da462005-03-21 11:47:04 +0000483 INIT_LIST_HEAD(&fc->background);
Miklos Szeredi3f0005f2005-01-04 19:24:31 +0000484 sema_init(&fc->outstanding_sem, 0);
Miklos Szeredid17da462005-03-21 11:47:04 +0000485 init_rwsem(&fc->sbput_sem);
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000486 for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
487 struct fuse_req *req = fuse_request_alloc();
488 if (!req) {
489 free_conn(fc);
490 return NULL;
491 }
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000492 list_add(&req->list, &fc->unused_list);
493 }
Miklos Szeredib6220192005-01-05 16:19:10 +0000494#ifdef KERNEL_2_6_6_PLUS
Miklos Szeredia25d4c22004-11-23 22:32:16 +0000495 fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
496 fc->bdi.unplug_io_fn = default_unplug_io_fn;
497#endif
Miklos Szeredie3b83092005-07-22 17:24:30 +0000498 fc->reqctr = 0;
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000499 }
500 return fc;
501}
502
Miklos Szeredied62d862004-06-20 08:57:39 +0000503static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000504{
Miklos Szeredied62d862004-06-20 08:57:39 +0000505 struct fuse_conn *fc;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000506
Miklos Szeredie56818b2004-12-12 11:45:24 +0000507 if (file->f_op != &fuse_dev_operations)
Miklos Szerediee808382005-01-21 22:05:37 +0000508 return ERR_PTR(-EINVAL);
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000509 fc = new_conn();
Miklos Szeredie56818b2004-12-12 11:45:24 +0000510 if (fc == NULL)
Miklos Szerediee808382005-01-21 22:05:37 +0000511 return ERR_PTR(-ENOMEM);
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000512 spin_lock(&fuse_lock);
513 if (file->private_data) {
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000514 free_conn(fc);
Miklos Szerediee808382005-01-21 22:05:37 +0000515 fc = ERR_PTR(-EINVAL);
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000516 } else {
517 file->private_data = fc;
Miklos Szeredi0111f9d2005-04-22 12:04:55 +0000518 *get_fuse_conn_super_p(sb) = fc;
519 fc->mounted = 1;
520 fc->connected = 1;
521 fc->count = 2;
Miklos Szeredi3d60e762004-11-11 14:44:04 +0000522 }
523 spin_unlock(&fuse_lock);
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000524 return fc;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000525}
526
Miklos Szeredi83a07442004-11-30 18:25:20 +0000527static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000528{
Miklos Szeredi5e183482001-10-31 14:52:35 +0000529 struct fuse_attr attr;
530 memset(&attr, 0, sizeof(attr));
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000531
Miklos Szeredi2df1c042001-11-06 15:07:17 +0000532 attr.mode = mode;
Miklos Szeredia13d9002004-11-02 17:32:03 +0000533 attr.ino = FUSE_ROOT_ID;
Miklos Szeredi38009022005-05-08 19:47:22 +0000534 return fuse_iget(sb, 1, 0, &attr);
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000535}
Miklos Szeredi1b188022005-07-28 11:07:29 +0000536#ifndef FUSE_MAINLINE
Miklos Szeredie815c032004-01-19 18:20:49 +0000537#ifdef KERNEL_2_6
Miklos Szeredie815c032004-01-19 18:20:49 +0000538static struct dentry *fuse_get_dentry(struct super_block *sb, void *vobjp)
539{
540 __u32 *objp = vobjp;
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000541 unsigned long nodeid = objp[0];
Miklos Szeredi76f65782004-02-19 16:55:40 +0000542 __u32 generation = objp[1];
Miklos Szeredie815c032004-01-19 18:20:49 +0000543 struct inode *inode;
544 struct dentry *entry;
545
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000546 if (nodeid == 0)
Miklos Szeredia5288792005-03-05 16:46:52 +0000547 return ERR_PTR(-ESTALE);
Miklos Szeredie815c032004-01-19 18:20:49 +0000548
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000549 inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
Miklos Szeredif43f0632005-02-28 11:46:56 +0000550 if (!inode)
Miklos Szeredia5288792005-03-05 16:46:52 +0000551 return ERR_PTR(-ESTALE);
552 if (inode->i_generation != generation) {
553 iput(inode);
554 return ERR_PTR(-ESTALE);
555 }
Miklos Szeredie815c032004-01-19 18:20:49 +0000556
557 entry = d_alloc_anon(inode);
Miklos Szeredia5288792005-03-05 16:46:52 +0000558 if (!entry) {
559 iput(inode);
560 return ERR_PTR(-ENOMEM);
561 }
Miklos Szeredie815c032004-01-19 18:20:49 +0000562
563 return entry;
564}
565
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000566static int fuse_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
567 int connectable)
568{
569 struct inode *inode = dentry->d_inode;
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000570 int len = *max_len;
571 int type = 1;
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000572
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000573 if (len < 2 || (connectable && len < 4))
574 return 255;
575
576 len = 2;
Miklos Szeredi039322d2004-12-01 18:39:12 +0000577 fh[0] = get_fuse_inode(inode)->nodeid;
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000578 fh[1] = inode->i_generation;
579 if (connectable && !S_ISDIR(inode->i_mode)) {
580 struct inode *parent;
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000581
582 spin_lock(&dentry->d_lock);
583 parent = dentry->d_parent->d_inode;
Miklos Szeredi039322d2004-12-01 18:39:12 +0000584 fh[2] = get_fuse_inode(parent)->nodeid;
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000585 fh[3] = parent->i_generation;
586 spin_unlock(&dentry->d_lock);
587 len = 4;
588 type = 2;
589 }
590 *max_len = len;
591 return type;
592}
593
Miklos Szeredie815c032004-01-19 18:20:49 +0000594static struct export_operations fuse_export_operations = {
595 .get_dentry = fuse_get_dentry,
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000596 .encode_fh = fuse_encode_fh,
Miklos Szeredie815c032004-01-19 18:20:49 +0000597};
598#endif
Miklos Szeredi1b188022005-07-28 11:07:29 +0000599#endif
Miklos Szeredie815c032004-01-19 18:20:49 +0000600
601static struct super_operations fuse_super_operations = {
Miklos Szeredia13d9002004-11-02 17:32:03 +0000602 .alloc_inode = fuse_alloc_inode,
603 .destroy_inode = fuse_destroy_inode,
Miklos Szeredie815c032004-01-19 18:20:49 +0000604 .read_inode = fuse_read_inode,
605 .clear_inode = fuse_clear_inode,
606 .put_super = fuse_put_super,
607 .statfs = fuse_statfs,
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000608 .show_options = fuse_show_options,
Miklos Szeredie815c032004-01-19 18:20:49 +0000609};
610
Miklos Szeredi81394522005-01-11 14:24:18 +0000611static int fuse_fill_super(struct super_block *sb, void *data, int silent)
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000612{
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000613 struct fuse_conn *fc;
614 struct inode *root;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000615 struct fuse_mount_data d;
Miklos Szeredied62d862004-06-20 08:57:39 +0000616 struct file *file;
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000617 int err;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000618
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000619 if (!parse_fuse_opt((char *) data, &d))
620 return -EINVAL;
621
Miklos Szeredi377b2ea2004-02-06 09:48:51 +0000622 sb->s_blocksize = PAGE_CACHE_SIZE;
623 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
624 sb->s_magic = FUSE_SUPER_MAGIC;
625 sb->s_op = &fuse_super_operations;
626 sb->s_maxbytes = MAX_LFS_FILESIZE;
Miklos Szeredi1b188022005-07-28 11:07:29 +0000627#ifndef FUSE_MAINLINE
Miklos Szeredie815c032004-01-19 18:20:49 +0000628#ifdef KERNEL_2_6
629 sb->s_export_op = &fuse_export_operations;
630#endif
Miklos Szeredi1b188022005-07-28 11:07:29 +0000631#endif
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000632
Miklos Szeredied62d862004-06-20 08:57:39 +0000633 file = fget(d.fd);
634 if (!file)
Miklos Szeredi307242f2004-01-26 11:28:44 +0000635 return -EINVAL;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000636
Miklos Szeredied62d862004-06-20 08:57:39 +0000637 fc = get_conn(file, sb);
Miklos Szeredied62d862004-06-20 08:57:39 +0000638 fput(file);
Miklos Szerediee808382005-01-21 22:05:37 +0000639 if (IS_ERR(fc))
640 return PTR_ERR(fc);
Miklos Szeredied62d862004-06-20 08:57:39 +0000641
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000642 fc->flags = d.flags;
Miklos Szeredi3c7d41b2005-01-09 20:05:27 +0000643 fc->user_id = d.user_id;
Miklos Szeredi9c1b68d2005-04-28 09:55:09 +0000644 fc->group_id = d.group_id;
Miklos Szerediad051c32004-07-02 09:22:50 +0000645 fc->max_read = d.max_read;
Miklos Szeredia25d4c22004-11-23 22:32:16 +0000646#ifdef KERNEL_2_6
647 if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
648 fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
649#endif
Miklos Szerediaa63b6b2004-12-03 13:24:35 +0000650
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000651 err = -ENOMEM;
Miklos Szeredi7f6bc892004-04-13 10:49:14 +0000652 root = get_root_inode(sb, d.rootmode);
Miklos Szeredie56818b2004-12-12 11:45:24 +0000653 if (root == NULL)
Miklos Szeredied62d862004-06-20 08:57:39 +0000654 goto err;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000655
Miklos Szeredi79b52f62001-10-24 14:37:13 +0000656 sb->s_root = d_alloc_root(root);
Miklos Szeredied62d862004-06-20 08:57:39 +0000657 if (!sb->s_root) {
658 iput(root);
659 goto err;
660 }
Miklos Szeredi3f0005f2005-01-04 19:24:31 +0000661 fuse_send_init(fc);
Miklos Szeredif85ab242004-01-07 12:16:45 +0000662 return 0;
Miklos Szeredied62d862004-06-20 08:57:39 +0000663
664 err:
665 spin_lock(&fuse_lock);
Miklos Szeredied62d862004-06-20 08:57:39 +0000666 fuse_release_conn(fc);
667 spin_unlock(&fuse_lock);
Miklos Szeredi0f62d722005-01-04 12:45:54 +0000668 return err;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000669}
670
Miklos Szeredif85ab242004-01-07 12:16:45 +0000671#ifdef KERNEL_2_6
672static struct super_block *fuse_get_sb(struct file_system_type *fs_type,
Miklos Szeredie8663f32004-01-13 15:33:12 +0000673 int flags, const char *dev_name,
674 void *raw_data)
Miklos Szeredif85ab242004-01-07 12:16:45 +0000675{
Miklos Szeredi81394522005-01-11 14:24:18 +0000676 return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super);
Miklos Szeredif85ab242004-01-07 12:16:45 +0000677}
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000678
Miklos Szeredif85ab242004-01-07 12:16:45 +0000679static struct file_system_type fuse_fs_type = {
Miklos Szeredi377b2ea2004-02-06 09:48:51 +0000680 .owner = THIS_MODULE,
681 .name = "fuse",
682 .get_sb = fuse_get_sb,
683 .kill_sb = kill_anon_super,
Miklos Szeredif85ab242004-01-07 12:16:45 +0000684};
685#else
686static struct super_block *fuse_read_super_compat(struct super_block *sb,
687 void *data, int silent)
688{
Miklos Szeredi81394522005-01-11 14:24:18 +0000689 int err = fuse_fill_super(sb, data, silent);
Miklos Szeredic26c14d2004-04-09 17:48:32 +0000690 if (err)
Miklos Szeredif85ab242004-01-07 12:16:45 +0000691 return NULL;
692 else
693 return sb;
694}
695
696static DECLARE_FSTYPE(fuse_fs_type, "fuse", fuse_read_super_compat, 0);
697#endif
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000698
Miklos Szeredi81394522005-01-11 14:24:18 +0000699static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep,
Miklos Szeredia13d9002004-11-02 17:32:03 +0000700 unsigned long flags)
701{
Miklos Szeredi13ed4822004-11-20 11:12:21 +0000702 struct inode * inode = foo;
Miklos Szeredia13d9002004-11-02 17:32:03 +0000703
704 if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
705 SLAB_CTOR_CONSTRUCTOR)
706 inode_init_once(inode);
707}
708
Miklos Szeredi9080c792005-01-08 11:50:08 +0000709static int __init fuse_fs_init(void)
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000710{
Miklos Szeredi069c9502004-07-16 16:17:02 +0000711 int err;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000712
Miklos Szeredi069c9502004-07-16 16:17:02 +0000713 err = register_filesystem(&fuse_fs_type);
714 if (err)
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000715 printk("fuse: failed to register filesystem\n");
Miklos Szeredi069c9502004-07-16 16:17:02 +0000716 else {
717 fuse_inode_cachep = kmem_cache_create("fuse_inode",
Miklos Szeredi81394522005-01-11 14:24:18 +0000718 sizeof(struct fuse_inode),
Miklos Szeredia13d9002004-11-02 17:32:03 +0000719 0, SLAB_HWCACHE_ALIGN,
720 fuse_inode_init_once, NULL);
Miklos Szeredi069c9502004-07-16 16:17:02 +0000721 if (!fuse_inode_cachep) {
722 unregister_filesystem(&fuse_fs_type);
723 err = -ENOMEM;
724 }
725 }
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000726
Miklos Szeredi069c9502004-07-16 16:17:02 +0000727 return err;
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000728}
729
Miklos Szeredi9080c792005-01-08 11:50:08 +0000730static void fuse_fs_cleanup(void)
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000731{
732 unregister_filesystem(&fuse_fs_type);
Miklos Szeredi069c9502004-07-16 16:17:02 +0000733 kmem_cache_destroy(fuse_inode_cachep);
Miklos Szeredibc22e7b2001-10-23 19:26:04 +0000734}
Miklos Szeredi9080c792005-01-08 11:50:08 +0000735
Miklos Szeredi81394522005-01-11 14:24:18 +0000736static int __init fuse_init(void)
Miklos Szeredi9080c792005-01-08 11:50:08 +0000737{
738 int res;
739
740 printk("fuse init (API version %i.%i)\n",
741 FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
742#ifndef FUSE_MAINLINE
743 printk("fuse distribution version: %s\n", FUSE_VERSION);
744#endif
745
746 spin_lock_init(&fuse_lock);
747 res = fuse_fs_init();
748 if (res)
749 goto err;
750
751 res = fuse_dev_init();
752 if (res)
753 goto err_fs_cleanup;
754
755 return 0;
756
757 err_fs_cleanup:
758 fuse_fs_cleanup();
759 err:
760 return res;
761}
762
Miklos Szeredi81394522005-01-11 14:24:18 +0000763static void __exit fuse_exit(void)
Miklos Szeredi9080c792005-01-08 11:50:08 +0000764{
765 printk(KERN_DEBUG "fuse exit\n");
766
767 fuse_fs_cleanup();
768 fuse_dev_cleanup();
769}
770
771module_init(fuse_init);
772module_exit(fuse_exit);