9p: attach-per-user
[h-e-n] / fs / 9p / v9fs.c
1 /*
2  *  linux/fs/9p/v9fs.c
3  *
4  *  This file contains functions assisting in mapping VFS to 9P2000
5  *
6  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2
11  *  as published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to:
20  *  Free Software Foundation
21  *  51 Franklin Street, Fifth Floor
22  *  Boston, MA  02111-1301  USA
23  *
24  */
25
26 #include <linux/module.h>
27 #include <linux/errno.h>
28 #include <linux/fs.h>
29 #include <linux/sched.h>
30 #include <linux/parser.h>
31 #include <linux/idr.h>
32 #include <net/9p/9p.h>
33 #include <net/9p/transport.h>
34 #include <net/9p/conn.h>
35 #include <net/9p/client.h>
36 #include "v9fs.h"
37 #include "v9fs_vfs.h"
38
39 /*
40  * Dynamic Transport Registration Routines
41  *
42  */
43
44 static LIST_HEAD(v9fs_trans_list);
45 static struct p9_trans_module *v9fs_default_trans;
46
47 /**
48  * v9fs_register_trans - register a new transport with 9p
49  * @m - structure describing the transport module and entry points
50  *
51  */
52 void v9fs_register_trans(struct p9_trans_module *m)
53 {
54         list_add_tail(&m->list, &v9fs_trans_list);
55         if (m->def)
56                 v9fs_default_trans = m;
57 }
58 EXPORT_SYMBOL(v9fs_register_trans);
59
60 /**
61  * v9fs_match_trans - match transport versus registered transports
62  * @arg: string identifying transport
63  *
64  */
65 static struct p9_trans_module *v9fs_match_trans(const substring_t *name)
66 {
67         struct list_head *p;
68         struct p9_trans_module *t = NULL;
69
70         list_for_each(p, &v9fs_trans_list) {
71                 t = list_entry(p, struct p9_trans_module, list);
72                 if (strncmp(t->name, name->from, name->to-name->from) == 0) {
73                         P9_DPRINTK(P9_DEBUG_TRANS, "trans=%s\n", t->name);
74                         break;
75                 }
76         }
77         return t;
78 }
79
80 /*
81   * Option Parsing (code inspired by NFS code)
82   *  NOTE: each transport will parse its own options
83   */
84
85 enum {
86         /* Options that take integer arguments */
87         Opt_debug, Opt_msize, Opt_dfltuid, Opt_dfltgid, Opt_afid,
88         /* String options */
89         Opt_uname, Opt_remotename, Opt_trans,
90         /* Options that take no arguments */
91         Opt_legacy, Opt_nodevmap,
92         /* Cache options */
93         Opt_cache_loose,
94         /* Access options */
95         Opt_access,
96         /* Error token */
97         Opt_err
98 };
99
100 static match_table_t tokens = {
101         {Opt_debug, "debug=%x"},
102         {Opt_msize, "msize=%u"},
103         {Opt_dfltuid, "dfltuid=%u"},
104         {Opt_dfltgid, "dfltgid=%u"},
105         {Opt_afid, "afid=%u"},
106         {Opt_uname, "uname=%s"},
107         {Opt_remotename, "aname=%s"},
108         {Opt_trans, "trans=%s"},
109         {Opt_legacy, "noextend"},
110         {Opt_nodevmap, "nodevmap"},
111         {Opt_cache_loose, "cache=loose"},
112         {Opt_cache_loose, "loose"},
113         {Opt_access, "access=%s"},
114         {Opt_err, NULL}
115 };
116
117 /**
118  * v9fs_parse_options - parse mount options into session structure
119  * @options: options string passed from mount
120  * @v9ses: existing v9fs session information
121  *
122  */
123
124 static void v9fs_parse_options(struct v9fs_session_info *v9ses)
125 {
126         char *options = v9ses->options;
127         substring_t args[MAX_OPT_ARGS];
128         char *p;
129         int option;
130         int ret;
131         char *s, *e;
132
133         /* setup defaults */
134         v9ses->maxdata = 8192;
135         v9ses->afid = ~0;
136         v9ses->debug = 0;
137         v9ses->cache = 0;
138         v9ses->trans = v9fs_default_trans;
139
140         if (!options)
141                 return;
142
143         while ((p = strsep(&options, ",")) != NULL) {
144                 int token;
145                 if (!*p)
146                         continue;
147                 token = match_token(p, tokens, args);
148                 if (token < Opt_uname) {
149                         if ((ret = match_int(&args[0], &option)) < 0) {
150                                 P9_DPRINTK(P9_DEBUG_ERROR,
151                                         "integer field, but no integer?\n");
152                                 continue;
153                         }
154                 }
155                 switch (token) {
156                 case Opt_debug:
157                         v9ses->debug = option;
158 #ifdef CONFIG_NET_9P_DEBUG
159                         p9_debug_level = option;
160 #endif
161                         break;
162                 case Opt_msize:
163                         v9ses->maxdata = option;
164                         break;
165                 case Opt_dfltuid:
166                         v9ses->dfltuid = option;
167                         break;
168                 case Opt_dfltgid:
169                         v9ses->dfltgid = option;
170                         break;
171                 case Opt_afid:
172                         v9ses->afid = option;
173                         break;
174                 case Opt_trans:
175                         v9ses->trans = v9fs_match_trans(&args[0]);
176                         break;
177                 case Opt_uname:
178                         match_strcpy(v9ses->uname, &args[0]);
179                         break;
180                 case Opt_remotename:
181                         match_strcpy(v9ses->aname, &args[0]);
182                         break;
183                 case Opt_legacy:
184                         v9ses->flags &= ~V9FS_EXTENDED;
185                         break;
186                 case Opt_nodevmap:
187                         v9ses->nodev = 1;
188                         break;
189                 case Opt_cache_loose:
190                         v9ses->cache = CACHE_LOOSE;
191                         break;
192
193                 case Opt_access:
194                         s = match_strdup(&args[0]);
195                         v9ses->flags &= ~V9FS_ACCESS_MASK;
196                         if (strcmp(s, "user") == 0)
197                                 v9ses->flags |= V9FS_ACCESS_USER;
198                         else if (strcmp(s, "any") == 0)
199                                 v9ses->flags |= V9FS_ACCESS_ANY;
200                         else {
201                                 v9ses->flags |= V9FS_ACCESS_SINGLE;
202                                 v9ses->uid = simple_strtol(s, &e, 10);
203                                 if (*e != '\0')
204                                         v9ses->uid = ~0;
205                         }
206                         break;
207
208                 default:
209                         continue;
210                 }
211         }
212 }
213
214 /**
215  * v9fs_session_init - initialize session
216  * @v9ses: session information structure
217  * @dev_name: device being mounted
218  * @data: options
219  *
220  */
221
222 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
223                   const char *dev_name, char *data)
224 {
225         int retval = -EINVAL;
226         struct p9_trans *trans = NULL;
227         struct p9_fid *fid;
228
229         v9ses->uname = __getname();
230         if (!v9ses->uname)
231                 return ERR_PTR(-ENOMEM);
232
233         v9ses->aname = __getname();
234         if (!v9ses->aname) {
235                 __putname(v9ses->uname);
236                 return ERR_PTR(-ENOMEM);
237         }
238
239         v9ses->flags = V9FS_EXTENDED | V9FS_ACCESS_USER;
240         strcpy(v9ses->uname, V9FS_DEFUSER);
241         strcpy(v9ses->aname, V9FS_DEFANAME);
242         v9ses->uid = ~0;
243         v9ses->dfltuid = V9FS_DEFUID;
244         v9ses->dfltgid = V9FS_DEFGID;
245         v9ses->options = kstrdup(data, GFP_KERNEL);
246         v9fs_parse_options(v9ses);
247
248         if ((v9ses->trans == NULL) && !list_empty(&v9fs_trans_list))
249                 v9ses->trans = list_first_entry(&v9fs_trans_list,
250                  struct p9_trans_module, list);
251
252         if (v9ses->trans == NULL) {
253                 retval = -EPROTONOSUPPORT;
254                 P9_DPRINTK(P9_DEBUG_ERROR,
255                                 "No transport defined or default transport\n");
256                 goto error;
257         }
258
259         trans = v9ses->trans->create(dev_name, v9ses->options);
260         if (IS_ERR(trans)) {
261                 retval = PTR_ERR(trans);
262                 trans = NULL;
263                 goto error;
264         }
265         if ((v9ses->maxdata+P9_IOHDRSZ) > v9ses->trans->maxsize)
266                 v9ses->maxdata = v9ses->trans->maxsize-P9_IOHDRSZ;
267
268         v9ses->clnt = p9_client_create(trans, v9ses->maxdata+P9_IOHDRSZ,
269                 v9fs_extended(v9ses));
270
271         if (IS_ERR(v9ses->clnt)) {
272                 retval = PTR_ERR(v9ses->clnt);
273                 v9ses->clnt = NULL;
274                 P9_DPRINTK(P9_DEBUG_ERROR, "problem initializing 9p client\n");
275                 goto error;
276         }
277
278         if (!v9ses->clnt->dotu)
279                 v9ses->flags &= ~V9FS_EXTENDED;
280
281         /* for legacy mode, fall back to V9FS_ACCESS_ANY */
282         if (!v9fs_extended(v9ses) &&
283                 ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
284
285                 v9ses->flags &= ~V9FS_ACCESS_MASK;
286                 v9ses->flags |= V9FS_ACCESS_ANY;
287                 v9ses->uid = ~0;
288         }
289
290         fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0,
291                                                         v9ses->aname);
292         if (IS_ERR(fid)) {
293                 retval = PTR_ERR(fid);
294                 fid = NULL;
295                 P9_DPRINTK(P9_DEBUG_ERROR, "cannot attach\n");
296                 goto error;
297         }
298
299         if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
300                 fid->uid = v9ses->uid;
301         else
302                 fid->uid = ~0;
303
304         return fid;
305
306 error:
307         v9fs_session_close(v9ses);
308         return ERR_PTR(retval);
309 }
310
311 /**
312  * v9fs_session_close - shutdown a session
313  * @v9ses: session information structure
314  *
315  */
316
317 void v9fs_session_close(struct v9fs_session_info *v9ses)
318 {
319         if (v9ses->clnt) {
320                 p9_client_destroy(v9ses->clnt);
321                 v9ses->clnt = NULL;
322         }
323
324         __putname(v9ses->uname);
325         __putname(v9ses->aname);
326         kfree(v9ses->options);
327 }
328
329 /**
330  * v9fs_session_cancel - mark transport as disconnected
331  *      and cancel all pending requests.
332  */
333 void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
334         P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
335         p9_client_disconnect(v9ses->clnt);
336 }
337
338 extern int v9fs_error_init(void);
339
340 /**
341  * v9fs_init - Initialize module
342  *
343  */
344
345 static int __init init_v9fs(void)
346 {
347         printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
348         /* TODO: Setup list of registered trasnport modules */
349         return register_filesystem(&v9fs_fs_type);
350 }
351
352 /**
353  * v9fs_init - shutdown module
354  *
355  */
356
357 static void __exit exit_v9fs(void)
358 {
359         unregister_filesystem(&v9fs_fs_type);
360 }
361
362 module_init(init_v9fs)
363 module_exit(exit_v9fs)
364
365 MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
366 MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
367 MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
368 MODULE_LICENSE("GPL");