Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/9p/v9fs.c
15111 views
1
/*
2
* linux/fs/9p/v9fs.c
3
*
4
* This file contains functions assisting in mapping VFS to 9P2000
5
*
6
* Copyright (C) 2004-2008 by Eric Van Hensbergen <[email protected]>
7
* Copyright (C) 2002 by Ron Minnich <[email protected]>
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 <linux/slab.h>
33
#include <net/9p/9p.h>
34
#include <net/9p/client.h>
35
#include <net/9p/transport.h>
36
#include "v9fs.h"
37
#include "v9fs_vfs.h"
38
#include "cache.h"
39
40
static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
41
static LIST_HEAD(v9fs_sessionlist);
42
struct kmem_cache *v9fs_inode_cache;
43
44
/*
45
* Option Parsing (code inspired by NFS code)
46
* NOTE: each transport will parse its own options
47
*/
48
49
enum {
50
/* Options that take integer arguments */
51
Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
52
/* String options */
53
Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
54
/* Options that take no arguments */
55
Opt_nodevmap,
56
/* Cache options */
57
Opt_cache_loose, Opt_fscache,
58
/* Access options */
59
Opt_access, Opt_posixacl,
60
/* Error token */
61
Opt_err
62
};
63
64
static const match_table_t tokens = {
65
{Opt_debug, "debug=%x"},
66
{Opt_dfltuid, "dfltuid=%u"},
67
{Opt_dfltgid, "dfltgid=%u"},
68
{Opt_afid, "afid=%u"},
69
{Opt_uname, "uname=%s"},
70
{Opt_remotename, "aname=%s"},
71
{Opt_nodevmap, "nodevmap"},
72
{Opt_cache, "cache=%s"},
73
{Opt_cache_loose, "loose"},
74
{Opt_fscache, "fscache"},
75
{Opt_cachetag, "cachetag=%s"},
76
{Opt_access, "access=%s"},
77
{Opt_posixacl, "posixacl"},
78
{Opt_err, NULL}
79
};
80
81
/**
82
* v9fs_parse_options - parse mount options into session structure
83
* @v9ses: existing v9fs session information
84
*
85
* Return 0 upon success, -ERRNO upon failure.
86
*/
87
88
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
89
{
90
char *options, *tmp_options;
91
substring_t args[MAX_OPT_ARGS];
92
char *p;
93
int option = 0;
94
char *s, *e;
95
int ret = 0;
96
97
/* setup defaults */
98
v9ses->afid = ~0;
99
v9ses->debug = 0;
100
v9ses->cache = 0;
101
#ifdef CONFIG_9P_FSCACHE
102
v9ses->cachetag = NULL;
103
#endif
104
105
if (!opts)
106
return 0;
107
108
tmp_options = kstrdup(opts, GFP_KERNEL);
109
if (!tmp_options) {
110
ret = -ENOMEM;
111
goto fail_option_alloc;
112
}
113
options = tmp_options;
114
115
while ((p = strsep(&options, ",")) != NULL) {
116
int token;
117
if (!*p)
118
continue;
119
token = match_token(p, tokens, args);
120
if (token < Opt_uname) {
121
int r = match_int(&args[0], &option);
122
if (r < 0) {
123
P9_DPRINTK(P9_DEBUG_ERROR,
124
"integer field, but no integer?\n");
125
ret = r;
126
continue;
127
}
128
}
129
switch (token) {
130
case Opt_debug:
131
v9ses->debug = option;
132
#ifdef CONFIG_NET_9P_DEBUG
133
p9_debug_level = option;
134
#endif
135
break;
136
137
case Opt_dfltuid:
138
v9ses->dfltuid = option;
139
break;
140
case Opt_dfltgid:
141
v9ses->dfltgid = option;
142
break;
143
case Opt_afid:
144
v9ses->afid = option;
145
break;
146
case Opt_uname:
147
match_strlcpy(v9ses->uname, &args[0], PATH_MAX);
148
break;
149
case Opt_remotename:
150
match_strlcpy(v9ses->aname, &args[0], PATH_MAX);
151
break;
152
case Opt_nodevmap:
153
v9ses->nodev = 1;
154
break;
155
case Opt_cache_loose:
156
v9ses->cache = CACHE_LOOSE;
157
break;
158
case Opt_fscache:
159
v9ses->cache = CACHE_FSCACHE;
160
break;
161
case Opt_cachetag:
162
#ifdef CONFIG_9P_FSCACHE
163
v9ses->cachetag = match_strdup(&args[0]);
164
#endif
165
break;
166
case Opt_cache:
167
s = match_strdup(&args[0]);
168
if (!s) {
169
ret = -ENOMEM;
170
P9_DPRINTK(P9_DEBUG_ERROR,
171
"problem allocating copy of cache arg\n");
172
goto free_and_return;
173
}
174
175
if (strcmp(s, "loose") == 0)
176
v9ses->cache = CACHE_LOOSE;
177
else if (strcmp(s, "fscache") == 0)
178
v9ses->cache = CACHE_FSCACHE;
179
else
180
v9ses->cache = CACHE_NONE;
181
kfree(s);
182
break;
183
184
case Opt_access:
185
s = match_strdup(&args[0]);
186
if (!s) {
187
ret = -ENOMEM;
188
P9_DPRINTK(P9_DEBUG_ERROR,
189
"problem allocating copy of access arg\n");
190
goto free_and_return;
191
}
192
193
v9ses->flags &= ~V9FS_ACCESS_MASK;
194
if (strcmp(s, "user") == 0)
195
v9ses->flags |= V9FS_ACCESS_USER;
196
else if (strcmp(s, "any") == 0)
197
v9ses->flags |= V9FS_ACCESS_ANY;
198
else if (strcmp(s, "client") == 0) {
199
v9ses->flags |= V9FS_ACCESS_CLIENT;
200
} else {
201
v9ses->flags |= V9FS_ACCESS_SINGLE;
202
v9ses->uid = simple_strtoul(s, &e, 10);
203
if (*e != '\0')
204
v9ses->uid = ~0;
205
}
206
kfree(s);
207
break;
208
209
case Opt_posixacl:
210
#ifdef CONFIG_9P_FS_POSIX_ACL
211
v9ses->flags |= V9FS_POSIX_ACL;
212
#else
213
P9_DPRINTK(P9_DEBUG_ERROR,
214
"Not defined CONFIG_9P_FS_POSIX_ACL. "
215
"Ignoring posixacl option\n");
216
#endif
217
break;
218
219
default:
220
continue;
221
}
222
}
223
224
free_and_return:
225
kfree(tmp_options);
226
fail_option_alloc:
227
return ret;
228
}
229
230
/**
231
* v9fs_session_init - initialize session
232
* @v9ses: session information structure
233
* @dev_name: device being mounted
234
* @data: options
235
*
236
*/
237
238
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
239
const char *dev_name, char *data)
240
{
241
int retval = -EINVAL;
242
struct p9_fid *fid;
243
int rc;
244
245
v9ses->uname = __getname();
246
if (!v9ses->uname)
247
return ERR_PTR(-ENOMEM);
248
249
v9ses->aname = __getname();
250
if (!v9ses->aname) {
251
__putname(v9ses->uname);
252
return ERR_PTR(-ENOMEM);
253
}
254
init_rwsem(&v9ses->rename_sem);
255
256
rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY);
257
if (rc) {
258
__putname(v9ses->aname);
259
__putname(v9ses->uname);
260
return ERR_PTR(rc);
261
}
262
263
spin_lock(&v9fs_sessionlist_lock);
264
list_add(&v9ses->slist, &v9fs_sessionlist);
265
spin_unlock(&v9fs_sessionlist_lock);
266
267
strcpy(v9ses->uname, V9FS_DEFUSER);
268
strcpy(v9ses->aname, V9FS_DEFANAME);
269
v9ses->uid = ~0;
270
v9ses->dfltuid = V9FS_DEFUID;
271
v9ses->dfltgid = V9FS_DEFGID;
272
273
v9ses->clnt = p9_client_create(dev_name, data);
274
if (IS_ERR(v9ses->clnt)) {
275
retval = PTR_ERR(v9ses->clnt);
276
v9ses->clnt = NULL;
277
P9_DPRINTK(P9_DEBUG_ERROR, "problem initializing 9p client\n");
278
goto error;
279
}
280
281
v9ses->flags = V9FS_ACCESS_USER;
282
283
if (p9_is_proto_dotl(v9ses->clnt)) {
284
v9ses->flags = V9FS_ACCESS_CLIENT;
285
v9ses->flags |= V9FS_PROTO_2000L;
286
} else if (p9_is_proto_dotu(v9ses->clnt)) {
287
v9ses->flags |= V9FS_PROTO_2000U;
288
}
289
290
rc = v9fs_parse_options(v9ses, data);
291
if (rc < 0) {
292
retval = rc;
293
goto error;
294
}
295
296
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
297
298
if (!v9fs_proto_dotl(v9ses) &&
299
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
300
/*
301
* We support ACCESS_CLIENT only for dotl.
302
* Fall back to ACCESS_USER
303
*/
304
v9ses->flags &= ~V9FS_ACCESS_MASK;
305
v9ses->flags |= V9FS_ACCESS_USER;
306
}
307
/*FIXME !! */
308
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
309
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
310
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
311
312
v9ses->flags &= ~V9FS_ACCESS_MASK;
313
v9ses->flags |= V9FS_ACCESS_ANY;
314
v9ses->uid = ~0;
315
}
316
if (!v9fs_proto_dotl(v9ses) ||
317
!((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
318
/*
319
* We support ACL checks on clinet only if the protocol is
320
* 9P2000.L and access is V9FS_ACCESS_CLIENT.
321
*/
322
v9ses->flags &= ~V9FS_ACL_MASK;
323
}
324
325
fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0,
326
v9ses->aname);
327
if (IS_ERR(fid)) {
328
retval = PTR_ERR(fid);
329
fid = NULL;
330
P9_DPRINTK(P9_DEBUG_ERROR, "cannot attach\n");
331
goto error;
332
}
333
334
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
335
fid->uid = v9ses->uid;
336
else
337
fid->uid = ~0;
338
339
#ifdef CONFIG_9P_FSCACHE
340
/* register the session for caching */
341
v9fs_cache_session_get_cookie(v9ses);
342
#endif
343
344
return fid;
345
346
error:
347
bdi_destroy(&v9ses->bdi);
348
return ERR_PTR(retval);
349
}
350
351
/**
352
* v9fs_session_close - shutdown a session
353
* @v9ses: session information structure
354
*
355
*/
356
357
void v9fs_session_close(struct v9fs_session_info *v9ses)
358
{
359
if (v9ses->clnt) {
360
p9_client_destroy(v9ses->clnt);
361
v9ses->clnt = NULL;
362
}
363
364
#ifdef CONFIG_9P_FSCACHE
365
if (v9ses->fscache) {
366
v9fs_cache_session_put_cookie(v9ses);
367
kfree(v9ses->cachetag);
368
}
369
#endif
370
__putname(v9ses->uname);
371
__putname(v9ses->aname);
372
373
bdi_destroy(&v9ses->bdi);
374
375
spin_lock(&v9fs_sessionlist_lock);
376
list_del(&v9ses->slist);
377
spin_unlock(&v9fs_sessionlist_lock);
378
}
379
380
/**
381
* v9fs_session_cancel - terminate a session
382
* @v9ses: session to terminate
383
*
384
* mark transport as disconnected and cancel all pending requests.
385
*/
386
387
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
388
P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
389
p9_client_disconnect(v9ses->clnt);
390
}
391
392
/**
393
* v9fs_session_begin_cancel - Begin terminate of a session
394
* @v9ses: session to terminate
395
*
396
* After this call we don't allow any request other than clunk.
397
*/
398
399
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses)
400
{
401
P9_DPRINTK(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses);
402
p9_client_begin_disconnect(v9ses->clnt);
403
}
404
405
extern int v9fs_error_init(void);
406
407
static struct kobject *v9fs_kobj;
408
409
#ifdef CONFIG_9P_FSCACHE
410
/**
411
* caches_show - list caches associated with a session
412
*
413
* Returns the size of buffer written.
414
*/
415
416
static ssize_t caches_show(struct kobject *kobj,
417
struct kobj_attribute *attr,
418
char *buf)
419
{
420
ssize_t n = 0, count = 0, limit = PAGE_SIZE;
421
struct v9fs_session_info *v9ses;
422
423
spin_lock(&v9fs_sessionlist_lock);
424
list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
425
if (v9ses->cachetag) {
426
n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
427
if (n < 0) {
428
count = n;
429
break;
430
}
431
432
count += n;
433
limit -= n;
434
}
435
}
436
437
spin_unlock(&v9fs_sessionlist_lock);
438
return count;
439
}
440
441
static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
442
#endif /* CONFIG_9P_FSCACHE */
443
444
static struct attribute *v9fs_attrs[] = {
445
#ifdef CONFIG_9P_FSCACHE
446
&v9fs_attr_cache.attr,
447
#endif
448
NULL,
449
};
450
451
static struct attribute_group v9fs_attr_group = {
452
.attrs = v9fs_attrs,
453
};
454
455
/**
456
* v9fs_sysfs_init - Initialize the v9fs sysfs interface
457
*
458
*/
459
460
static int v9fs_sysfs_init(void)
461
{
462
v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
463
if (!v9fs_kobj)
464
return -ENOMEM;
465
466
if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
467
kobject_put(v9fs_kobj);
468
return -ENOMEM;
469
}
470
471
return 0;
472
}
473
474
/**
475
* v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
476
*
477
*/
478
479
static void v9fs_sysfs_cleanup(void)
480
{
481
sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
482
kobject_put(v9fs_kobj);
483
}
484
485
static void v9fs_inode_init_once(void *foo)
486
{
487
struct v9fs_inode *v9inode = (struct v9fs_inode *)foo;
488
#ifdef CONFIG_9P_FSCACHE
489
v9inode->fscache = NULL;
490
v9inode->fscache_key = NULL;
491
#endif
492
inode_init_once(&v9inode->vfs_inode);
493
}
494
495
/**
496
* v9fs_init_inode_cache - initialize a cache for 9P
497
* Returns 0 on success.
498
*/
499
static int v9fs_init_inode_cache(void)
500
{
501
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
502
sizeof(struct v9fs_inode),
503
0, (SLAB_RECLAIM_ACCOUNT|
504
SLAB_MEM_SPREAD),
505
v9fs_inode_init_once);
506
if (!v9fs_inode_cache)
507
return -ENOMEM;
508
509
return 0;
510
}
511
512
/**
513
* v9fs_destroy_inode_cache - destroy the cache of 9P inode
514
*
515
*/
516
static void v9fs_destroy_inode_cache(void)
517
{
518
kmem_cache_destroy(v9fs_inode_cache);
519
}
520
521
static int v9fs_cache_register(void)
522
{
523
int ret;
524
ret = v9fs_init_inode_cache();
525
if (ret < 0)
526
return ret;
527
#ifdef CONFIG_9P_FSCACHE
528
return fscache_register_netfs(&v9fs_cache_netfs);
529
#else
530
return ret;
531
#endif
532
}
533
534
static void v9fs_cache_unregister(void)
535
{
536
v9fs_destroy_inode_cache();
537
#ifdef CONFIG_9P_FSCACHE
538
fscache_unregister_netfs(&v9fs_cache_netfs);
539
#endif
540
}
541
542
/**
543
* init_v9fs - Initialize module
544
*
545
*/
546
547
static int __init init_v9fs(void)
548
{
549
int err;
550
printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
551
/* TODO: Setup list of registered trasnport modules */
552
err = register_filesystem(&v9fs_fs_type);
553
if (err < 0) {
554
printk(KERN_ERR "Failed to register filesystem\n");
555
return err;
556
}
557
558
err = v9fs_cache_register();
559
if (err < 0) {
560
printk(KERN_ERR "Failed to register v9fs for caching\n");
561
goto out_fs_unreg;
562
}
563
564
err = v9fs_sysfs_init();
565
if (err < 0) {
566
printk(KERN_ERR "Failed to register with sysfs\n");
567
goto out_sysfs_cleanup;
568
}
569
570
return 0;
571
572
out_sysfs_cleanup:
573
v9fs_sysfs_cleanup();
574
575
out_fs_unreg:
576
unregister_filesystem(&v9fs_fs_type);
577
578
return err;
579
}
580
581
/**
582
* exit_v9fs - shutdown module
583
*
584
*/
585
586
static void __exit exit_v9fs(void)
587
{
588
v9fs_sysfs_cleanup();
589
v9fs_cache_unregister();
590
unregister_filesystem(&v9fs_fs_type);
591
}
592
593
module_init(init_v9fs)
594
module_exit(exit_v9fs)
595
596
MODULE_AUTHOR("Latchesar Ionkov <[email protected]>");
597
MODULE_AUTHOR("Eric Van Hensbergen <[email protected]>");
598
MODULE_AUTHOR("Ron Minnich <[email protected]>");
599
MODULE_LICENSE("GPL");
600
601